diff options
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 97199d6ec79f..c28e5c287447 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -57,9 +57,71 @@ | |||
57 | 57 | ||
58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; | 58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; |
59 | 59 | ||
60 | static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | ||
61 | { | ||
62 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
63 | struct ipv6hdr *ipv6h; | ||
64 | struct inet6_protocol *ops; | ||
65 | int proto; | ||
66 | |||
67 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
68 | goto out; | ||
69 | |||
70 | ipv6h = skb->nh.ipv6h; | ||
71 | proto = ipv6h->nexthdr; | ||
72 | __skb_pull(skb, sizeof(*ipv6h)); | ||
73 | |||
74 | rcu_read_lock(); | ||
75 | for (;;) { | ||
76 | struct ipv6_opt_hdr *opth; | ||
77 | int len; | ||
78 | |||
79 | if (proto != NEXTHDR_HOP) { | ||
80 | ops = rcu_dereference(inet6_protos[proto]); | ||
81 | |||
82 | if (unlikely(!ops)) | ||
83 | goto unlock; | ||
84 | |||
85 | if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | if (unlikely(!pskb_may_pull(skb, 8))) | ||
90 | goto unlock; | ||
91 | |||
92 | opth = (void *)skb->data; | ||
93 | len = opth->hdrlen * 8 + 8; | ||
94 | |||
95 | if (unlikely(!pskb_may_pull(skb, len))) | ||
96 | goto unlock; | ||
97 | |||
98 | proto = opth->nexthdr; | ||
99 | __skb_pull(skb, len); | ||
100 | } | ||
101 | |||
102 | skb->h.raw = skb->data; | ||
103 | if (likely(ops->gso_segment)) | ||
104 | segs = ops->gso_segment(skb, features); | ||
105 | |||
106 | unlock: | ||
107 | rcu_read_unlock(); | ||
108 | |||
109 | if (unlikely(IS_ERR(segs))) | ||
110 | goto out; | ||
111 | |||
112 | for (skb = segs; skb; skb = skb->next) { | ||
113 | ipv6h = skb->nh.ipv6h; | ||
114 | ipv6h->payload_len = htons(skb->len - skb->mac_len); | ||
115 | } | ||
116 | |||
117 | out: | ||
118 | return segs; | ||
119 | } | ||
120 | |||
60 | static struct packet_type ipv6_packet_type = { | 121 | static struct packet_type ipv6_packet_type = { |
61 | .type = __constant_htons(ETH_P_IPV6), | 122 | .type = __constant_htons(ETH_P_IPV6), |
62 | .func = ipv6_rcv, | 123 | .func = ipv6_rcv, |
124 | .gso_segment = ipv6_gso_segment, | ||
63 | }; | 125 | }; |
64 | 126 | ||
65 | struct ip6_ra_chain *ip6_ra_chain; | 127 | struct ip6_ra_chain *ip6_ra_chain; |