]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/ipv6/af_inet6.c
netfilter: xtables: ignore unassigned hooks in check_entry_size_and_hooks
[mv-sheeva.git] / net / ipv6 / af_inet6.c
index 85b3d0036afdeff08efc32d1179dbabf7a58f9b3..bf85d5f97032412294023a3acbdfb43cff63023c 100644 (file)
@@ -772,6 +772,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        struct ipv6hdr *ipv6h;
        struct inet6_protocol *ops;
+       int proto;
+       struct frag_hdr *fptr;
+       unsigned int unfrag_ip6hlen;
+       u8 *prevhdr;
+       int offset = 0;
 
        if (!(features & NETIF_F_V6_CSUM))
                features &= ~NETIF_F_SG;
@@ -791,10 +796,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
        __skb_pull(skb, sizeof(*ipv6h));
        segs = ERR_PTR(-EPROTONOSUPPORT);
 
+       proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
        rcu_read_lock();
-       ops = rcu_dereference(inet6_protos[
-               ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
-
+       ops = rcu_dereference(inet6_protos[proto]);
        if (likely(ops && ops->gso_segment)) {
                skb_reset_transport_header(skb);
                segs = ops->gso_segment(skb, features);
@@ -808,6 +812,16 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
                ipv6h = ipv6_hdr(skb);
                ipv6h->payload_len = htons(skb->len - skb->mac_len -
                                           sizeof(*ipv6h));
+               if (proto == IPPROTO_UDP) {
+                       unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+                       fptr = (struct frag_hdr *)(skb_network_header(skb) +
+                               unfrag_ip6hlen);
+                       fptr->frag_off = htons(offset);
+                       if (skb->next != NULL)
+                               fptr->frag_off |= htons(IP6_MF);
+                       offset += (ntohs(ipv6h->payload_len) -
+                                  sizeof(struct frag_hdr));
+               }
        }
 
 out:
@@ -1284,6 +1298,8 @@ static void __exit inet6_exit(void)
        proto_unregister(&udplitev6_prot);
        proto_unregister(&udpv6_prot);
        proto_unregister(&tcpv6_prot);
+
+       rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
 module_exit(inet6_exit);