Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-gso-restore-outer-ip-ids-correctly'

Richard Gobert says:

====================
net: gso: restore outer ip ids correctly

GRO currently ignores outer IPv4 header IDs for encapsulated packets
that have their don't-fragment flag set. GSO, however, always assumes
that outer IP IDs are incrementing. This results in GSO mangling the
outer IDs when they aren't incrementing. For example, GSO mangles the
outer IDs of IPv6 packets that were converted to IPv4, which must
have an ID of 0 according to RFC 6145, sect. 5.1.

GRO+GSO is supposed to be entirely transparent by default. GSO already
correctly restores inner IDs and IDs of non-encapsulated packets. The
tx-tcp-mangleid-segmentation feature can be enabled to allow the
mangling of such IDs so that TSO can be used.

This series fixes outer ID restoration for encapsulated packets when
tx-tcp-mangleid-segmentation is disabled. It also allows GRO to merge
packets with fixed IDs that don't have their don't-fragment flag set.

v1: https://lore.kernel.org/netdev/20250814114030.7683-1-richardbgobert@gmail.com/
v2: https://lore.kernel.org/netdev/20250819063223.5239-1-richardbgobert@gmail.com/
v3: https://lore.kernel.org/netdev/20250821073047.2091-1-richardbgobert@gmail.com/
v4: https://lore.kernel.org/netdev/20250901113826.6508-1-richardbgobert@gmail.com/
v5: https://lore.kernel.org/netdev/20250915113933.3293-1-richardbgobert@gmail.com/
v6: https://lore.kernel.org/netdev/20250916144841.4884-1-richardbgobert@gmail.com/
v7: https://lore.kernel.org/netdev/20250922084103.4764-1-richardbgobert@gmail.com/
====================

Link: https://patch.msgid.link/20250923085908.4687-1-richardbgobert@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+129 -84
+14 -8
Documentation/networking/segmentation-offloads.rst
··· 43 43 For IPv4 segmentation we support one of two types in terms of the IP ID. 44 44 The default behavior is to increment the IP ID with every segment. If the 45 45 GSO type SKB_GSO_TCP_FIXEDID is specified then we will not increment the IP 46 - ID and all segments will use the same IP ID. If a device has 47 - NETIF_F_TSO_MANGLEID set then the IP ID can be ignored when performing TSO 48 - and we will either increment the IP ID for all frames, or leave it at a 49 - static value based on driver preference. 46 + ID and all segments will use the same IP ID. 47 + 48 + For encapsulated packets, SKB_GSO_TCP_FIXEDID refers only to the outer header. 49 + SKB_GSO_TCP_FIXEDID_INNER can be used to specify the same for the inner header. 50 + Any combination of these two GSO types is allowed. 51 + 52 + If a device has NETIF_F_TSO_MANGLEID set then the IP ID can be ignored when 53 + performing TSO and we will either increment the IP ID for all frames, or leave 54 + it at a static value based on driver preference. For encapsulated packets, 55 + NETIF_F_TSO_MANGLEID is relevant for both outer and inner headers, unless the 56 + DF bit is not set on the outer header, in which case the device driver must 57 + guarantee that the IP ID field is incremented in the outer header with every 58 + segment. 50 59 51 60 52 61 UDP Fragmentation Offload ··· 133 124 Generic receive offload is the complement to GSO. Ideally any frame 134 125 assembled by GRO should be segmented to create an identical sequence of 135 126 frames using GSO, and any sequence of frames segmented by GSO should be 136 - able to be reassembled back to the original by GRO. The only exception to 137 - this is IPv4 ID in the case that the DF bit is set for a given IP header. 138 - If the value of the IPv4 ID is not sequentially incrementing it will be 139 - altered so that it is when a frame assembled via GRO is segmented via GSO. 127 + able to be reassembled back to the original by GRO. 140 128 141 129 142 130 Partial Generic Segmentation Offload
+6 -2
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
··· 1290 1290 tcp->check = ~tcp_v4_check(skb->len - tcp_off, ipv4->saddr, 1291 1291 ipv4->daddr, 0); 1292 1292 skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4; 1293 - if (ntohs(ipv4->id) == rq->hw_gro_data->second_ip_id) 1294 - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_FIXEDID; 1293 + if (ntohs(ipv4->id) == rq->hw_gro_data->second_ip_id) { 1294 + bool encap = rq->hw_gro_data->fk.control.flags & FLOW_DIS_ENCAPSULATION; 1295 + 1296 + skb_shinfo(skb)->gso_type |= encap ? SKB_GSO_TCP_FIXEDID_INNER : 1297 + SKB_GSO_TCP_FIXEDID; 1298 + } 1295 1299 1296 1300 skb->csum_start = (unsigned char *)tcp - skb->head; 1297 1301 skb->csum_offset = offsetof(struct tcphdr, check);
+13 -4
drivers/net/ethernet/sfc/ef100_tx.c
··· 189 189 { 190 190 bool gso_partial = skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL; 191 191 unsigned int len, ip_offset, tcp_offset, payload_segs; 192 + u32 mangleid_outer = ESE_GZ_TX_DESC_IP4_ID_INC_MOD16; 192 193 u32 mangleid = ESE_GZ_TX_DESC_IP4_ID_INC_MOD16; 193 194 unsigned int outer_ip_offset, outer_l4_offset; 194 195 u16 vlan_tci = skb_vlan_tag_get(skb); ··· 201 200 bool outer_csum; 202 201 u32 paylen; 203 202 204 - if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID) 205 - mangleid = ESE_GZ_TX_DESC_IP4_ID_NO_OP; 203 + if (encap) { 204 + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID_INNER) 205 + mangleid = ESE_GZ_TX_DESC_IP4_ID_NO_OP; 206 + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID) 207 + mangleid_outer = ESE_GZ_TX_DESC_IP4_ID_NO_OP; 208 + } else { 209 + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID) 210 + mangleid = ESE_GZ_TX_DESC_IP4_ID_NO_OP; 211 + mangleid_outer = ESE_GZ_TX_DESC_IP4_ID_NO_OP; 212 + } 213 + 206 214 if (efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_TX) 207 215 vlan_enable = skb_vlan_tag_present(skb); 208 216 ··· 255 245 ESF_GZ_TX_TSO_OUTER_L4_OFF_W, outer_l4_offset >> 1, 256 246 ESF_GZ_TX_TSO_ED_OUTER_UDP_LEN, udp_encap && !gso_partial, 257 247 ESF_GZ_TX_TSO_ED_OUTER_IP_LEN, encap && !gso_partial, 258 - ESF_GZ_TX_TSO_ED_OUTER_IP4_ID, encap ? mangleid : 259 - ESE_GZ_TX_DESC_IP4_ID_NO_OP, 248 + ESF_GZ_TX_TSO_ED_OUTER_IP4_ID, mangleid_outer, 260 249 ESF_GZ_TX_TSO_VLAN_INSERT_EN, vlan_enable, 261 250 ESF_GZ_TX_TSO_VLAN_INSERT_TCI, vlan_tci 262 251 );
+7 -2
include/linux/netdevice.h
··· 5320 5320 5321 5321 static inline bool net_gso_ok(netdev_features_t features, int gso_type) 5322 5322 { 5323 - netdev_features_t feature = (netdev_features_t)gso_type << NETIF_F_GSO_SHIFT; 5323 + netdev_features_t feature; 5324 + 5325 + if (gso_type & (SKB_GSO_TCP_FIXEDID | SKB_GSO_TCP_FIXEDID_INNER)) 5326 + gso_type |= __SKB_GSO_TCP_FIXEDID; 5327 + 5328 + feature = ((netdev_features_t)gso_type << NETIF_F_GSO_SHIFT) & NETIF_F_GSO_MASK; 5324 5329 5325 5330 /* check flags correspondence */ 5326 5331 BUILD_BUG_ON(SKB_GSO_TCPV4 != (NETIF_F_TSO >> NETIF_F_GSO_SHIFT)); 5327 5332 BUILD_BUG_ON(SKB_GSO_DODGY != (NETIF_F_GSO_ROBUST >> NETIF_F_GSO_SHIFT)); 5328 5333 BUILD_BUG_ON(SKB_GSO_TCP_ECN != (NETIF_F_TSO_ECN >> NETIF_F_GSO_SHIFT)); 5329 - BUILD_BUG_ON(SKB_GSO_TCP_FIXEDID != (NETIF_F_TSO_MANGLEID >> NETIF_F_GSO_SHIFT)); 5334 + BUILD_BUG_ON(__SKB_GSO_TCP_FIXEDID != (NETIF_F_TSO_MANGLEID >> NETIF_F_GSO_SHIFT)); 5330 5335 BUILD_BUG_ON(SKB_GSO_TCPV6 != (NETIF_F_TSO6 >> NETIF_F_GSO_SHIFT)); 5331 5336 BUILD_BUG_ON(SKB_GSO_FCOE != (NETIF_F_FSO >> NETIF_F_GSO_SHIFT)); 5332 5337 BUILD_BUG_ON(SKB_GSO_GRE != (NETIF_F_GSO_GRE >> NETIF_F_GSO_SHIFT));
+7 -1
include/linux/skbuff.h
··· 674 674 /* This indicates the tcp segment has CWR set. */ 675 675 SKB_GSO_TCP_ECN = 1 << 2, 676 676 677 - SKB_GSO_TCP_FIXEDID = 1 << 3, 677 + __SKB_GSO_TCP_FIXEDID = 1 << 3, 678 678 679 679 SKB_GSO_TCPV6 = 1 << 4, 680 680 ··· 707 707 SKB_GSO_FRAGLIST = 1 << 18, 708 708 709 709 SKB_GSO_TCP_ACCECN = 1 << 19, 710 + 711 + /* These indirectly map onto the same netdev feature. 712 + * If NETIF_F_TSO_MANGLEID is set it may mangle both inner and outer IDs. 713 + */ 714 + SKB_GSO_TCP_FIXEDID = 1 << 30, 715 + SKB_GSO_TCP_FIXEDID_INNER = 1 << 31, 710 716 }; 711 717 712 718 #if BITS_PER_LONG > 32
+12 -20
include/net/gro.h
··· 71 71 /* Free the skb? */ 72 72 u8 free:2; 73 73 74 - /* Used in foo-over-udp, set in udp[46]_gro_receive */ 75 - u8 is_ipv6:1; 76 - 77 74 /* Used in GRE, set in fou/gue_gro_receive */ 78 75 u8 is_fou:1; 79 76 80 77 /* Used to determine if ipid_offset can be ignored */ 81 - u8 ip_fixedid:1; 78 + u8 ip_fixedid:2; 82 79 83 80 /* Number of gro_receive callbacks this packet already went through */ 84 81 u8 recursion_counter:4; ··· 442 445 } 443 446 444 447 static inline int inet_gro_flush(const struct iphdr *iph, const struct iphdr *iph2, 445 - struct sk_buff *p, bool outer) 448 + struct sk_buff *p, bool inner) 446 449 { 447 450 const u32 id = ntohl(*(__be32 *)&iph->id); 448 451 const u32 id2 = ntohl(*(__be32 *)&iph2->id); 449 452 const u16 ipid_offset = (id >> 16) - (id2 >> 16); 450 453 const u16 count = NAPI_GRO_CB(p)->count; 451 - const u32 df = id & IP_DF; 452 - int flush; 453 454 454 455 /* All fields must match except length and checksum. */ 455 - flush = (iph->ttl ^ iph2->ttl) | (iph->tos ^ iph2->tos) | (df ^ (id2 & IP_DF)); 456 - 457 - if (flush | (outer && df)) 458 - return flush; 456 + if ((iph->ttl ^ iph2->ttl) | (iph->tos ^ iph2->tos) | ((id ^ id2) & IP_DF)) 457 + return true; 459 458 460 459 /* When we receive our second frame we can make a decision on if we 461 460 * continue this flow as an atomic flow with a fixed ID or if we use 462 461 * an incrementing ID. 463 462 */ 464 - if (count == 1 && df && !ipid_offset) 465 - NAPI_GRO_CB(p)->ip_fixedid = true; 463 + if (count == 1 && !ipid_offset) 464 + NAPI_GRO_CB(p)->ip_fixedid |= 1 << inner; 466 465 467 - return ipid_offset ^ (count * !NAPI_GRO_CB(p)->ip_fixedid); 466 + return ipid_offset ^ (count * !(NAPI_GRO_CB(p)->ip_fixedid & (1 << inner))); 468 467 } 469 468 470 469 static inline int ipv6_gro_flush(const struct ipv6hdr *iph, const struct ipv6hdr *iph2) ··· 475 482 476 483 static inline int __gro_receive_network_flush(const void *th, const void *th2, 477 484 struct sk_buff *p, const u16 diff, 478 - bool outer) 485 + bool inner) 479 486 { 480 487 const void *nh = th - diff; 481 488 const void *nh2 = th2 - diff; ··· 483 490 if (((struct iphdr *)nh)->version == 6) 484 491 return ipv6_gro_flush(nh, nh2); 485 492 else 486 - return inet_gro_flush(nh, nh2, p, outer); 493 + return inet_gro_flush(nh, nh2, p, inner); 487 494 } 488 495 489 496 static inline int gro_receive_network_flush(const void *th, const void *th2, 490 497 struct sk_buff *p) 491 498 { 492 - const bool encap_mark = NAPI_GRO_CB(p)->encap_mark; 493 499 int off = skb_transport_offset(p); 494 500 int flush; 495 501 496 - flush = __gro_receive_network_flush(th, th2, p, off - NAPI_GRO_CB(p)->network_offset, encap_mark); 497 - if (encap_mark) 498 - flush |= __gro_receive_network_flush(th, th2, p, off - NAPI_GRO_CB(p)->inner_network_offset, false); 502 + flush = __gro_receive_network_flush(th, th2, p, off - NAPI_GRO_CB(p)->network_offset, false); 503 + if (NAPI_GRO_CB(p)->encap_mark) 504 + flush |= __gro_receive_network_flush(th, th2, p, off - NAPI_GRO_CB(p)->inner_network_offset, true); 499 505 500 506 return flush; 501 507 }
+8 -2
net/core/dev.c
··· 3768 3768 if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL)) 3769 3769 features &= ~dev->gso_partial_features; 3770 3770 3771 - /* Make sure to clear the IPv4 ID mangling feature if the 3772 - * IPv4 header has the potential to be fragmented. 3771 + /* Make sure to clear the IPv4 ID mangling feature if the IPv4 header 3772 + * has the potential to be fragmented so that TSO does not generate 3773 + * segments with the same ID. For encapsulated packets, the ID mangling 3774 + * feature is guaranteed not to use the same ID for the outer IPv4 3775 + * headers of the generated segments if the headers have the potential 3776 + * to be fragmented, so there is no need to clear the IPv4 ID mangling 3777 + * feature (see the section about NETIF_F_TSO_MANGLEID in 3778 + * segmentation-offloads.rst). 3773 3779 */ 3774 3780 if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { 3775 3781 struct iphdr *iph = skb->encapsulation ?
+3 -7
net/ipv4/af_inet.c
··· 1395 1395 1396 1396 segs = ERR_PTR(-EPROTONOSUPPORT); 1397 1397 1398 - if (!skb->encapsulation || encap) { 1399 - udpfrag = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP); 1400 - fixedid = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID); 1398 + fixedid = !!(skb_shinfo(skb)->gso_type & (SKB_GSO_TCP_FIXEDID << encap)); 1401 1399 1402 - /* fixed ID is invalid if DF bit is not set */ 1403 - if (fixedid && !(ip_hdr(skb)->frag_off & htons(IP_DF))) 1404 - goto out; 1405 - } 1400 + if (!skb->encapsulation || encap) 1401 + udpfrag = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP); 1406 1402 1407 1403 ops = rcu_dereference(inet_offloads[proto]); 1408 1404 if (likely(ops && ops->callbacks.gso_segment)) {
+14 -18
net/ipv4/fou_core.c
··· 228 228 return 0; 229 229 } 230 230 231 + static const struct net_offload *fou_gro_ops(const struct sock *sk, 232 + int proto) 233 + { 234 + const struct net_offload __rcu **offloads; 235 + 236 + /* FOU doesn't allow IPv4 on IPv6 sockets. */ 237 + offloads = sk->sk_family == AF_INET6 ? inet6_offloads : inet_offloads; 238 + return rcu_dereference(offloads[proto]); 239 + } 240 + 231 241 static struct sk_buff *fou_gro_receive(struct sock *sk, 232 242 struct list_head *head, 233 243 struct sk_buff *skb) 234 244 { 235 - const struct net_offload __rcu **offloads; 236 245 struct fou *fou = fou_from_sock(sk); 237 246 const struct net_offload *ops; 238 247 struct sk_buff *pp = NULL; 239 - u8 proto; 240 248 241 249 if (!fou) 242 250 goto out; 243 - 244 - proto = fou->protocol; 245 251 246 252 /* We can clear the encap_mark for FOU as we are essentially doing 247 253 * one of two possible things. We are either adding an L4 tunnel ··· 260 254 /* Flag this frame as already having an outer encap header */ 261 255 NAPI_GRO_CB(skb)->is_fou = 1; 262 256 263 - offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; 264 - ops = rcu_dereference(offloads[proto]); 257 + ops = fou_gro_ops(sk, fou->protocol); 265 258 if (!ops || !ops->callbacks.gro_receive) 266 259 goto out; 267 260 ··· 273 268 static int fou_gro_complete(struct sock *sk, struct sk_buff *skb, 274 269 int nhoff) 275 270 { 276 - const struct net_offload __rcu **offloads; 277 271 struct fou *fou = fou_from_sock(sk); 278 272 const struct net_offload *ops; 279 - u8 proto; 280 273 int err; 281 274 282 275 if (!fou) { ··· 282 279 goto out; 283 280 } 284 281 285 - proto = fou->protocol; 286 - 287 - offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; 288 - ops = rcu_dereference(offloads[proto]); 282 + ops = fou_gro_ops(sk, fou->protocol); 289 283 if (WARN_ON(!ops || !ops->callbacks.gro_complete)) { 290 284 err = -ENOSYS; 291 285 goto out; ··· 323 323 struct list_head *head, 324 324 struct sk_buff *skb) 325 325 { 326 - const struct net_offload __rcu **offloads; 327 326 const struct net_offload *ops; 328 327 struct sk_buff *pp = NULL; 329 328 struct sk_buff *p; ··· 449 450 /* Flag this frame as already having an outer encap header */ 450 451 NAPI_GRO_CB(skb)->is_fou = 1; 451 452 452 - offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; 453 - ops = rcu_dereference(offloads[proto]); 453 + ops = fou_gro_ops(sk, proto); 454 454 if (!ops || !ops->callbacks.gro_receive) 455 455 goto out; 456 456 ··· 465 467 static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) 466 468 { 467 469 struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff); 468 - const struct net_offload __rcu **offloads; 469 470 const struct net_offload *ops; 470 471 unsigned int guehlen = 0; 471 472 u8 proto; ··· 491 494 return err; 492 495 } 493 496 494 - offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; 495 - ops = rcu_dereference(offloads[proto]); 497 + ops = fou_gro_ops(sk, proto); 496 498 if (WARN_ON(!ops || !ops->callbacks.gro_complete)) 497 499 goto out; 498 500
+1
net/ipv4/tcp_offload.c
··· 484 484 th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr, 485 485 iph->daddr, 0); 486 486 487 + BUILD_BUG_ON(SKB_GSO_TCP_FIXEDID << 1 != SKB_GSO_TCP_FIXEDID_INNER); 487 488 skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4 | 488 489 (NAPI_GRO_CB(skb)->ip_fixedid * SKB_GSO_TCP_FIXEDID); 489 490
-2
net/ipv4/udp_offload.c
··· 891 891 skb_gro_checksum_try_convert(skb, IPPROTO_UDP, 892 892 inet_gro_compute_pseudo); 893 893 skip: 894 - NAPI_GRO_CB(skb)->is_ipv6 = 0; 895 - 896 894 if (static_branch_unlikely(&udp_encap_needed_key)) 897 895 sk = udp4_gro_lookup_skb(skb, uh->source, uh->dest); 898 896
-2
net/ipv6/udp_offload.c
··· 154 154 ip6_gro_compute_pseudo); 155 155 156 156 skip: 157 - NAPI_GRO_CB(skb)->is_ipv6 = 1; 158 - 159 157 if (static_branch_unlikely(&udpv6_encap_needed_key)) 160 158 sk = udp6_gro_lookup_skb(skb, uh->source, uh->dest); 161 159
+43 -15
tools/testing/selftests/net/gro.c
··· 93 93 static int tcp_offset = -1; 94 94 static int total_hdr_len = -1; 95 95 static int ethhdr_proto = -1; 96 + static bool ipip; 96 97 static const int num_flush_id_cases = 6; 97 98 98 99 static void vlog(const char *fmt, ...) ··· 115 114 int ipproto_off, opt_ipproto_off; 116 115 int next_off; 117 116 118 - if (proto == PF_INET) 117 + if (ipip) 118 + next_off = sizeof(struct iphdr) + offsetof(struct iphdr, protocol); 119 + else if (proto == PF_INET) 119 120 next_off = offsetof(struct iphdr, protocol); 120 121 else 121 122 next_off = offsetof(struct ipv6hdr, nexthdr); ··· 247 244 eth->h_proto = ethhdr_proto; 248 245 } 249 246 250 - static void fill_networklayer(void *buf, int payload_len) 247 + static void fill_networklayer(void *buf, int payload_len, int protocol) 251 248 { 252 249 struct ipv6hdr *ip6h = buf; 253 250 struct iphdr *iph = buf; ··· 257 254 258 255 ip6h->version = 6; 259 256 ip6h->payload_len = htons(sizeof(struct tcphdr) + payload_len); 260 - ip6h->nexthdr = IPPROTO_TCP; 257 + ip6h->nexthdr = protocol; 261 258 ip6h->hop_limit = 8; 262 259 if (inet_pton(AF_INET6, addr6_src, &ip6h->saddr) != 1) 263 260 error(1, errno, "inet_pton source ip6"); ··· 269 266 iph->version = 4; 270 267 iph->ihl = 5; 271 268 iph->ttl = 8; 272 - iph->protocol = IPPROTO_TCP; 269 + iph->protocol = protocol; 273 270 iph->tot_len = htons(sizeof(struct tcphdr) + 274 271 payload_len + sizeof(struct iphdr)); 275 272 iph->frag_off = htons(0x4000); /* DF = 1, MF = 0 */ ··· 316 313 { 317 314 memset(buf, 0, total_hdr_len); 318 315 memset(buf + total_hdr_len, 'a', payload_len); 316 + 319 317 fill_transportlayer(buf + tcp_offset, seq_offset, ack_offset, 320 318 payload_len, fin); 321 - fill_networklayer(buf + ETH_HLEN, payload_len); 319 + 320 + if (ipip) { 321 + fill_networklayer(buf + ETH_HLEN, payload_len + sizeof(struct iphdr), 322 + IPPROTO_IPIP); 323 + fill_networklayer(buf + ETH_HLEN + sizeof(struct iphdr), 324 + payload_len, IPPROTO_TCP); 325 + } else { 326 + fill_networklayer(buf + ETH_HLEN, payload_len, IPPROTO_TCP); 327 + } 328 + 322 329 fill_datalinklayer(buf); 323 330 } 324 331 ··· 429 416 iph->tot_len = htons(ntohs(iph->tot_len) + extlen); 430 417 iph->check = 0; 431 418 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0); 419 + 420 + if (ipip) { 421 + iph += 1; 422 + iph->tot_len = htons(ntohs(iph->tot_len) + extlen); 423 + iph->check = 0; 424 + iph->check = checksum_fold(iph, sizeof(struct iphdr), 0); 425 + } 432 426 } else { 433 427 ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen); 434 428 } ··· 690 670 iph2->id = htons(9); 691 671 break; 692 672 693 - case 3: /* DF=0, Fixed - should not coalesce */ 673 + case 3: /* DF=0, Fixed - should coalesce */ 694 674 iph1->frag_off &= ~htons(IP_DF); 695 675 iph1->id = htons(8); 696 676 ··· 797 777 */ 798 778 memset(buf + total_hdr_len, 'a', PAYLOAD_LEN * 2); 799 779 fill_transportlayer(buf + tcp_offset, PAYLOAD_LEN, 0, PAYLOAD_LEN * 2, 0); 800 - fill_networklayer(buf + ETH_HLEN, PAYLOAD_LEN); 780 + fill_networklayer(buf + ETH_HLEN, PAYLOAD_LEN, IPPROTO_TCP); 801 781 fill_datalinklayer(buf); 802 782 803 783 iph->frag_off = htons(0x6000); // DF = 1, MF = 1 ··· 1091 1071 * and min ipv6hdr size. Like MAX_HDR_SIZE, 1092 1072 * MAX_PAYLOAD is defined with the larger header of the two. 1093 1073 */ 1094 - int offset = proto == PF_INET ? 20 : 0; 1074 + int offset = (proto == PF_INET && !ipip) ? 20 : 0; 1095 1075 int remainder = (MAX_PAYLOAD + offset) % MSS; 1096 1076 1097 1077 send_large(txfd, &daddr, remainder); ··· 1208 1188 correct_payload[0] = PAYLOAD_LEN * 2; 1209 1189 check_recv_pkts(rxfd, correct_payload, 1); 1210 1190 1211 - printf("DF=0, Fixed - should not coalesce: "); 1212 - correct_payload[0] = PAYLOAD_LEN; 1213 - correct_payload[1] = PAYLOAD_LEN; 1214 - check_recv_pkts(rxfd, correct_payload, 2); 1191 + printf("DF=0, Fixed - should coalesce: "); 1192 + correct_payload[0] = PAYLOAD_LEN * 2; 1193 + check_recv_pkts(rxfd, correct_payload, 1); 1215 1194 1216 1195 printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: "); 1217 1196 correct_payload[0] = PAYLOAD_LEN * 2; ··· 1241 1222 check_recv_pkts(rxfd, correct_payload, 2); 1242 1223 } 1243 1224 } else if (strcmp(testname, "large") == 0) { 1244 - int offset = proto == PF_INET ? 20 : 0; 1225 + int offset = (proto == PF_INET && !ipip) ? 20 : 0; 1245 1226 int remainder = (MAX_PAYLOAD + offset) % MSS; 1246 1227 1247 1228 correct_payload[0] = (MAX_PAYLOAD + offset); ··· 1270 1251 { "iface", required_argument, NULL, 'i' }, 1271 1252 { "ipv4", no_argument, NULL, '4' }, 1272 1253 { "ipv6", no_argument, NULL, '6' }, 1254 + { "ipip", no_argument, NULL, 'e' }, 1273 1255 { "rx", no_argument, NULL, 'r' }, 1274 1256 { "saddr", required_argument, NULL, 's' }, 1275 1257 { "smac", required_argument, NULL, 'S' }, ··· 1280 1260 }; 1281 1261 int c; 1282 1262 1283 - while ((c = getopt_long(argc, argv, "46d:D:i:rs:S:t:v", opts, NULL)) != -1) { 1263 + while ((c = getopt_long(argc, argv, "46d:D:ei:rs:S:t:v", opts, NULL)) != -1) { 1284 1264 switch (c) { 1285 1265 case '4': 1286 1266 proto = PF_INET; ··· 1289 1269 case '6': 1290 1270 proto = PF_INET6; 1291 1271 ethhdr_proto = htons(ETH_P_IPV6); 1272 + break; 1273 + case 'e': 1274 + ipip = true; 1275 + proto = PF_INET; 1276 + ethhdr_proto = htons(ETH_P_IP); 1292 1277 break; 1293 1278 case 'd': 1294 1279 addr4_dst = addr6_dst = optarg; ··· 1330 1305 { 1331 1306 parse_args(argc, argv); 1332 1307 1333 - if (proto == PF_INET) { 1308 + if (ipip) { 1309 + tcp_offset = ETH_HLEN + sizeof(struct iphdr) * 2; 1310 + total_hdr_len = tcp_offset + sizeof(struct tcphdr); 1311 + } else if (proto == PF_INET) { 1334 1312 tcp_offset = ETH_HLEN + sizeof(struct iphdr); 1335 1313 total_hdr_len = tcp_offset + sizeof(struct tcphdr); 1336 1314 } else if (proto == PF_INET6) {
+1 -1
tools/testing/selftests/net/gro.sh
··· 4 4 readonly SERVER_MAC="aa:00:00:00:00:02" 5 5 readonly CLIENT_MAC="aa:00:00:00:00:01" 6 6 readonly TESTS=("data" "ack" "flags" "tcp" "ip" "large") 7 - readonly PROTOS=("ipv4" "ipv6") 7 + readonly PROTOS=("ipv4" "ipv6" "ipip") 8 8 dev="" 9 9 test="all" 10 10 proto="ipv4"