.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * IPV6 GSO/GRO offload support |
---|
3 | 4 | * Linux INET6 implementation |
---|
4 | | - * |
---|
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. |
---|
9 | 5 | */ |
---|
10 | 6 | |
---|
11 | 7 | #include <linux/kernel.h> |
---|
.. | .. |
---|
17 | 13 | #include <net/protocol.h> |
---|
18 | 14 | #include <net/ipv6.h> |
---|
19 | 15 | #include <net/inet_common.h> |
---|
| 16 | +#include <net/tcp.h> |
---|
| 17 | +#include <net/udp.h> |
---|
20 | 18 | |
---|
21 | 19 | #include "ip6_offload.h" |
---|
| 20 | + |
---|
| 21 | +/* All GRO functions are always builtin, except UDP over ipv6, which lays in |
---|
| 22 | + * ipv6 module, as it depends on UDPv6 lookup function, so we need special care |
---|
| 23 | + * when ipv6 is built as a module |
---|
| 24 | + */ |
---|
| 25 | +#if IS_BUILTIN(CONFIG_IPV6) |
---|
| 26 | +#define INDIRECT_CALL_L4(f, f2, f1, ...) INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__) |
---|
| 27 | +#else |
---|
| 28 | +#define INDIRECT_CALL_L4(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__) |
---|
| 29 | +#endif |
---|
| 30 | + |
---|
| 31 | +#define indirect_call_gro_receive_l4(f2, f1, cb, head, skb) \ |
---|
| 32 | +({ \ |
---|
| 33 | + unlikely(gro_recursion_inc_test(skb)) ? \ |
---|
| 34 | + NAPI_GRO_CB(skb)->flush |= 1, NULL : \ |
---|
| 35 | + INDIRECT_CALL_L4(cb, f2, f1, head, skb); \ |
---|
| 36 | +}) |
---|
22 | 37 | |
---|
23 | 38 | static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) |
---|
24 | 39 | { |
---|
.. | .. |
---|
166 | 181 | return len; |
---|
167 | 182 | } |
---|
168 | 183 | |
---|
169 | | -static struct sk_buff *ipv6_gro_receive(struct list_head *head, |
---|
170 | | - struct sk_buff *skb) |
---|
| 184 | +INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head, |
---|
| 185 | + struct sk_buff *skb) |
---|
171 | 186 | { |
---|
172 | 187 | const struct net_offload *ops; |
---|
173 | 188 | struct sk_buff *pp = NULL; |
---|
.. | .. |
---|
231 | 246 | * XXX skbs on the gro_list have all been parsed and pulled |
---|
232 | 247 | * already so we don't need to compare nlen |
---|
233 | 248 | * (nlen != (sizeof(*iph2) + ipv6_exthdrs_len(iph2, &ops))) |
---|
234 | | - * memcmp() alone below is suffcient, right? |
---|
| 249 | + * memcmp() alone below is sufficient, right? |
---|
235 | 250 | */ |
---|
236 | 251 | if ((first_word & htonl(0xF00FFFFF)) || |
---|
237 | | - memcmp(&iph->nexthdr, &iph2->nexthdr, |
---|
238 | | - nlen - offsetof(struct ipv6hdr, nexthdr))) { |
---|
| 252 | + !ipv6_addr_equal(&iph->saddr, &iph2->saddr) || |
---|
| 253 | + !ipv6_addr_equal(&iph->daddr, &iph2->daddr) || |
---|
| 254 | + *(u16 *)&iph->nexthdr != *(u16 *)&iph2->nexthdr) { |
---|
| 255 | +not_same_flow: |
---|
239 | 256 | NAPI_GRO_CB(p)->same_flow = 0; |
---|
240 | 257 | continue; |
---|
| 258 | + } |
---|
| 259 | + if (unlikely(nlen > sizeof(struct ipv6hdr))) { |
---|
| 260 | + if (memcmp(iph + 1, iph2 + 1, |
---|
| 261 | + nlen - sizeof(struct ipv6hdr))) |
---|
| 262 | + goto not_same_flow; |
---|
241 | 263 | } |
---|
242 | 264 | /* flush if Traffic Class fields are different */ |
---|
243 | 265 | NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000)); |
---|
.. | .. |
---|
255 | 277 | |
---|
256 | 278 | skb_gro_postpull_rcsum(skb, iph, nlen); |
---|
257 | 279 | |
---|
258 | | - pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); |
---|
| 280 | + pp = indirect_call_gro_receive_l4(tcp6_gro_receive, udp6_gro_receive, |
---|
| 281 | + ops->callbacks.gro_receive, head, skb); |
---|
259 | 282 | |
---|
260 | 283 | out_unlock: |
---|
261 | 284 | rcu_read_unlock(); |
---|
.. | .. |
---|
296 | 319 | return inet_gro_receive(head, skb); |
---|
297 | 320 | } |
---|
298 | 321 | |
---|
299 | | -static int ipv6_gro_complete(struct sk_buff *skb, int nhoff) |
---|
| 322 | +INDIRECT_CALLABLE_SCOPE int ipv6_gro_complete(struct sk_buff *skb, int nhoff) |
---|
300 | 323 | { |
---|
301 | 324 | const struct net_offload *ops; |
---|
302 | 325 | struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff); |
---|
.. | .. |
---|
315 | 338 | if (WARN_ON(!ops || !ops->callbacks.gro_complete)) |
---|
316 | 339 | goto out_unlock; |
---|
317 | 340 | |
---|
318 | | - err = ops->callbacks.gro_complete(skb, nhoff); |
---|
| 341 | + err = INDIRECT_CALL_L4(ops->callbacks.gro_complete, tcp6_gro_complete, |
---|
| 342 | + udp6_gro_complete, skb, nhoff); |
---|
319 | 343 | |
---|
320 | 344 | out_unlock: |
---|
321 | 345 | rcu_read_unlock(); |
---|
.. | .. |
---|
353 | 377 | }, |
---|
354 | 378 | }; |
---|
355 | 379 | |
---|
| 380 | +static struct sk_buff *sit_gso_segment(struct sk_buff *skb, |
---|
| 381 | + netdev_features_t features) |
---|
| 382 | +{ |
---|
| 383 | + if (!(skb_shinfo(skb)->gso_type & SKB_GSO_IPXIP4)) |
---|
| 384 | + return ERR_PTR(-EINVAL); |
---|
| 385 | + |
---|
| 386 | + return ipv6_gso_segment(skb, features); |
---|
| 387 | +} |
---|
| 388 | + |
---|
| 389 | +static struct sk_buff *ip4ip6_gso_segment(struct sk_buff *skb, |
---|
| 390 | + netdev_features_t features) |
---|
| 391 | +{ |
---|
| 392 | + if (!(skb_shinfo(skb)->gso_type & SKB_GSO_IPXIP6)) |
---|
| 393 | + return ERR_PTR(-EINVAL); |
---|
| 394 | + |
---|
| 395 | + return inet_gso_segment(skb, features); |
---|
| 396 | +} |
---|
| 397 | + |
---|
| 398 | +static struct sk_buff *ip6ip6_gso_segment(struct sk_buff *skb, |
---|
| 399 | + netdev_features_t features) |
---|
| 400 | +{ |
---|
| 401 | + if (!(skb_shinfo(skb)->gso_type & SKB_GSO_IPXIP6)) |
---|
| 402 | + return ERR_PTR(-EINVAL); |
---|
| 403 | + |
---|
| 404 | + return ipv6_gso_segment(skb, features); |
---|
| 405 | +} |
---|
| 406 | + |
---|
356 | 407 | static const struct net_offload sit_offload = { |
---|
357 | 408 | .callbacks = { |
---|
358 | | - .gso_segment = ipv6_gso_segment, |
---|
| 409 | + .gso_segment = sit_gso_segment, |
---|
359 | 410 | .gro_receive = sit_ip6ip6_gro_receive, |
---|
360 | 411 | .gro_complete = sit_gro_complete, |
---|
361 | 412 | }, |
---|
.. | .. |
---|
363 | 414 | |
---|
364 | 415 | static const struct net_offload ip4ip6_offload = { |
---|
365 | 416 | .callbacks = { |
---|
366 | | - .gso_segment = inet_gso_segment, |
---|
| 417 | + .gso_segment = ip4ip6_gso_segment, |
---|
367 | 418 | .gro_receive = ip4ip6_gro_receive, |
---|
368 | 419 | .gro_complete = ip4ip6_gro_complete, |
---|
369 | 420 | }, |
---|
.. | .. |
---|
371 | 422 | |
---|
372 | 423 | static const struct net_offload ip6ip6_offload = { |
---|
373 | 424 | .callbacks = { |
---|
374 | | - .gso_segment = ipv6_gso_segment, |
---|
| 425 | + .gso_segment = ip6ip6_gso_segment, |
---|
375 | 426 | .gro_receive = sit_ip6ip6_gro_receive, |
---|
376 | 427 | .gro_complete = ip6ip6_gro_complete, |
---|
377 | 428 | }, |
---|