2 * IPV6 GSO/GRO offload support
3 * Linux INET6 implementation
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
11 #include <linux/kernel.h>
12 #include <linux/socket.h>
13 #include <linux/netdevice.h>
14 #include <linux/skbuff.h>
15 #include <linux/printk.h>
17 #include <net/protocol.h>
19 #include <net/inet_common.h>
21 #include "ip6_offload.h"
23 static int ipv6_gso_pull_exthdrs(struct sk_buff
*skb
, int proto
)
25 const struct net_offload
*ops
= NULL
;
28 struct ipv6_opt_hdr
*opth
;
31 if (proto
!= NEXTHDR_HOP
) {
32 ops
= rcu_dereference(inet6_offloads
[proto
]);
37 if (!(ops
->flags
& INET6_PROTO_GSO_EXTHDR
))
41 if (unlikely(!pskb_may_pull(skb
, 8)))
44 opth
= (void *)skb
->data
;
45 len
= ipv6_optlen(opth
);
47 if (unlikely(!pskb_may_pull(skb
, len
)))
50 opth
= (void *)skb
->data
;
51 proto
= opth
->nexthdr
;
58 static struct sk_buff
*ipv6_gso_segment(struct sk_buff
*skb
,
59 netdev_features_t features
)
61 struct sk_buff
*segs
= ERR_PTR(-EINVAL
);
62 struct ipv6hdr
*ipv6h
;
63 const struct net_offload
*ops
;
65 struct frag_hdr
*fptr
;
66 unsigned int payload_len
;
73 skb_reset_network_header(skb
);
74 nhoff
= skb_network_header(skb
) - skb_mac_header(skb
);
75 if (unlikely(!pskb_may_pull(skb
, sizeof(*ipv6h
))))
78 encap
= SKB_GSO_CB(skb
)->encap_level
> 0;
80 features
&= skb
->dev
->hw_enc_features
;
81 SKB_GSO_CB(skb
)->encap_level
+= sizeof(*ipv6h
);
83 ipv6h
= ipv6_hdr(skb
);
84 __skb_pull(skb
, sizeof(*ipv6h
));
85 segs
= ERR_PTR(-EPROTONOSUPPORT
);
87 proto
= ipv6_gso_pull_exthdrs(skb
, ipv6h
->nexthdr
);
89 if (skb
->encapsulation
&&
90 skb_shinfo(skb
)->gso_type
& (SKB_GSO_IPXIP4
| SKB_GSO_IPXIP6
))
91 udpfrag
= proto
== IPPROTO_UDP
&& encap
&&
92 (skb_shinfo(skb
)->gso_type
& SKB_GSO_UDP
);
94 udpfrag
= proto
== IPPROTO_UDP
&& !skb
->encapsulation
&&
95 (skb_shinfo(skb
)->gso_type
& SKB_GSO_UDP
);
97 ops
= rcu_dereference(inet6_offloads
[proto
]);
98 if (likely(ops
&& ops
->callbacks
.gso_segment
)) {
99 skb_reset_transport_header(skb
);
100 segs
= ops
->callbacks
.gso_segment(skb
, features
);
103 if (IS_ERR_OR_NULL(segs
))
106 gso_partial
= !!(skb_shinfo(segs
)->gso_type
& SKB_GSO_PARTIAL
);
108 for (skb
= segs
; skb
; skb
= skb
->next
) {
109 ipv6h
= (struct ipv6hdr
*)(skb_mac_header(skb
) + nhoff
);
110 if (gso_partial
&& skb_is_gso(skb
))
111 payload_len
= skb_shinfo(skb
)->gso_size
+
112 SKB_GSO_CB(skb
)->data_offset
+
113 skb
->head
- (unsigned char *)(ipv6h
+ 1);
115 payload_len
= skb
->len
- nhoff
- sizeof(*ipv6h
);
116 ipv6h
->payload_len
= htons(payload_len
);
117 skb
->network_header
= (u8
*)ipv6h
- skb
->head
;
118 skb_reset_mac_len(skb
);
121 int err
= ip6_find_1stfragopt(skb
, &prevhdr
);
123 kfree_skb_list(segs
);
126 fptr
= (struct frag_hdr
*)((u8
*)ipv6h
+ err
);
127 fptr
->frag_off
= htons(offset
);
129 fptr
->frag_off
|= htons(IP6_MF
);
130 offset
+= (ntohs(ipv6h
->payload_len
) -
131 sizeof(struct frag_hdr
));
134 skb_reset_inner_headers(skb
);
141 /* Return the total length of all the extension hdrs, following the same
142 * logic in ipv6_gso_pull_exthdrs() when parsing ext-hdrs.
144 static int ipv6_exthdrs_len(struct ipv6hdr
*iph
,
145 const struct net_offload
**opps
)
147 struct ipv6_opt_hdr
*opth
= (void *)iph
;
148 int len
= 0, proto
, optlen
= sizeof(*iph
);
150 proto
= iph
->nexthdr
;
152 if (proto
!= NEXTHDR_HOP
) {
153 *opps
= rcu_dereference(inet6_offloads
[proto
]);
154 if (unlikely(!(*opps
)))
156 if (!((*opps
)->flags
& INET6_PROTO_GSO_EXTHDR
))
159 opth
= (void *)opth
+ optlen
;
160 optlen
= ipv6_optlen(opth
);
162 proto
= opth
->nexthdr
;
167 static struct sk_buff
*ipv6_gro_receive(struct list_head
*head
,
170 const struct net_offload
*ops
;
171 struct sk_buff
*pp
= NULL
;
180 off
= skb_gro_offset(skb
);
181 hlen
= off
+ sizeof(*iph
);
182 iph
= skb_gro_header_fast(skb
, off
);
183 if (skb_gro_header_hard(skb
, hlen
)) {
184 iph
= skb_gro_header_slow(skb
, hlen
, off
);
189 skb_set_network_header(skb
, off
);
190 skb_gro_pull(skb
, sizeof(*iph
));
191 skb_set_transport_header(skb
, skb_gro_offset(skb
));
193 flush
+= ntohs(iph
->payload_len
) != skb_gro_len(skb
);
196 proto
= iph
->nexthdr
;
197 ops
= rcu_dereference(inet6_offloads
[proto
]);
198 if (!ops
|| !ops
->callbacks
.gro_receive
) {
199 __pskb_pull(skb
, skb_gro_offset(skb
));
200 skb_gro_frag0_invalidate(skb
);
201 proto
= ipv6_gso_pull_exthdrs(skb
, proto
);
202 skb_gro_pull(skb
, -skb_transport_offset(skb
));
203 skb_reset_transport_header(skb
);
204 __skb_push(skb
, skb_gro_offset(skb
));
206 ops
= rcu_dereference(inet6_offloads
[proto
]);
207 if (!ops
|| !ops
->callbacks
.gro_receive
)
213 NAPI_GRO_CB(skb
)->proto
= proto
;
216 nlen
= skb_network_header_len(skb
);
218 list_for_each_entry(p
, head
, list
) {
219 const struct ipv6hdr
*iph2
;
220 __be32 first_word
; /* <Version:4><Traffic_Class:8><Flow_Label:20> */
222 if (!NAPI_GRO_CB(p
)->same_flow
)
225 iph2
= (struct ipv6hdr
*)(p
->data
+ off
);
226 first_word
= *(__be32
*)iph
^ *(__be32
*)iph2
;
228 /* All fields must match except length and Traffic Class.
229 * XXX skbs on the gro_list have all been parsed and pulled
230 * already so we don't need to compare nlen
231 * (nlen != (sizeof(*iph2) + ipv6_exthdrs_len(iph2, &ops)))
232 * memcmp() alone below is suffcient, right?
234 if ((first_word
& htonl(0xF00FFFFF)) ||
235 memcmp(&iph
->nexthdr
, &iph2
->nexthdr
,
236 nlen
- offsetof(struct ipv6hdr
, nexthdr
))) {
237 NAPI_GRO_CB(p
)->same_flow
= 0;
240 /* flush if Traffic Class fields are different */
241 NAPI_GRO_CB(p
)->flush
|= !!(first_word
& htonl(0x0FF00000));
242 NAPI_GRO_CB(p
)->flush
|= flush
;
244 /* If the previous IP ID value was based on an atomic
245 * datagram we can overwrite the value and ignore it.
247 if (NAPI_GRO_CB(skb
)->is_atomic
)
248 NAPI_GRO_CB(p
)->flush_id
= 0;
251 NAPI_GRO_CB(skb
)->is_atomic
= true;
252 NAPI_GRO_CB(skb
)->flush
|= flush
;
254 skb_gro_postpull_rcsum(skb
, iph
, nlen
);
256 pp
= call_gro_receive(ops
->callbacks
.gro_receive
, head
, skb
);
262 skb_gro_flush_final(skb
, pp
, flush
);
267 static struct sk_buff
*sit_ip6ip6_gro_receive(struct list_head
*head
,
270 /* Common GRO receive for SIT and IP6IP6 */
272 if (NAPI_GRO_CB(skb
)->encap_mark
) {
273 NAPI_GRO_CB(skb
)->flush
= 1;
277 NAPI_GRO_CB(skb
)->encap_mark
= 1;
279 return ipv6_gro_receive(head
, skb
);
282 static struct sk_buff
*ip4ip6_gro_receive(struct list_head
*head
,
285 /* Common GRO receive for SIT and IP6IP6 */
287 if (NAPI_GRO_CB(skb
)->encap_mark
) {
288 NAPI_GRO_CB(skb
)->flush
= 1;
292 NAPI_GRO_CB(skb
)->encap_mark
= 1;
294 return inet_gro_receive(head
, skb
);
297 static int ipv6_gro_complete(struct sk_buff
*skb
, int nhoff
)
299 const struct net_offload
*ops
;
300 struct ipv6hdr
*iph
= (struct ipv6hdr
*)(skb
->data
+ nhoff
);
303 if (skb
->encapsulation
) {
304 skb_set_inner_protocol(skb
, cpu_to_be16(ETH_P_IPV6
));
305 skb_set_inner_network_header(skb
, nhoff
);
308 iph
->payload_len
= htons(skb
->len
- nhoff
- sizeof(*iph
));
312 nhoff
+= sizeof(*iph
) + ipv6_exthdrs_len(iph
, &ops
);
313 if (WARN_ON(!ops
|| !ops
->callbacks
.gro_complete
))
316 err
= ops
->callbacks
.gro_complete(skb
, nhoff
);
324 static int sit_gro_complete(struct sk_buff
*skb
, int nhoff
)
326 skb
->encapsulation
= 1;
327 skb_shinfo(skb
)->gso_type
|= SKB_GSO_IPXIP4
;
328 return ipv6_gro_complete(skb
, nhoff
);
331 static int ip6ip6_gro_complete(struct sk_buff
*skb
, int nhoff
)
333 skb
->encapsulation
= 1;
334 skb_shinfo(skb
)->gso_type
|= SKB_GSO_IPXIP6
;
335 return ipv6_gro_complete(skb
, nhoff
);
338 static int ip4ip6_gro_complete(struct sk_buff
*skb
, int nhoff
)
340 skb
->encapsulation
= 1;
341 skb_shinfo(skb
)->gso_type
|= SKB_GSO_IPXIP6
;
342 return inet_gro_complete(skb
, nhoff
);
345 static struct packet_offload ipv6_packet_offload __read_mostly
= {
346 .type
= cpu_to_be16(ETH_P_IPV6
),
348 .gso_segment
= ipv6_gso_segment
,
349 .gro_receive
= ipv6_gro_receive
,
350 .gro_complete
= ipv6_gro_complete
,
354 static const struct net_offload sit_offload
= {
356 .gso_segment
= ipv6_gso_segment
,
357 .gro_receive
= sit_ip6ip6_gro_receive
,
358 .gro_complete
= sit_gro_complete
,
362 static const struct net_offload ip4ip6_offload
= {
364 .gso_segment
= inet_gso_segment
,
365 .gro_receive
= ip4ip6_gro_receive
,
366 .gro_complete
= ip4ip6_gro_complete
,
370 static const struct net_offload ip6ip6_offload
= {
372 .gso_segment
= ipv6_gso_segment
,
373 .gro_receive
= sit_ip6ip6_gro_receive
,
374 .gro_complete
= ip6ip6_gro_complete
,
377 static int __init
ipv6_offload_init(void)
380 if (tcpv6_offload_init() < 0)
381 pr_crit("%s: Cannot add TCP protocol offload\n", __func__
);
382 if (ipv6_exthdrs_offload_init() < 0)
383 pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__
);
385 dev_add_offload(&ipv6_packet_offload
);
387 inet_add_offload(&sit_offload
, IPPROTO_IPV6
);
388 inet6_add_offload(&ip6ip6_offload
, IPPROTO_IPV6
);
389 inet6_add_offload(&ip4ip6_offload
, IPPROTO_IPIP
);
394 fs_initcall(ipv6_offload_init
);