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

net: gro: parse ipv6 ext headers without frag0 invalidation

The existing code always pulls the IPv6 header and sets the transport
offset initially. Then optionally again pulls any extension headers in
ipv6_gso_pull_exthdrs and sets the transport offset again on return from
that call. skb->data is set at the start of the first extension header
before calling ipv6_gso_pull_exthdrs, and must disable the frag0
optimization because that function uses pskb_may_pull/pskb_pull instead of
skb_gro_ helpers. It sets the GRO offset to the TCP header with
skb_gro_pull and sets the transport header. Then returns skb->data to its
position before this block.

This commit introduces a new helper function - ipv6_gro_pull_exthdrs -
which is used in ipv6_gro_receive to pull ipv6 ext headers instead of
ipv6_gso_pull_exthdrs. Thus, there is no modification of skb->data, all
operations use skb_gro_* helpers, and the frag0 fast path can be taken for
IPv6 packets with ext headers.

Signed-off-by: Richard Gobert <richardbgobert@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://lore.kernel.org/r/504130f6-b56c-4dcc-882c-97942c59f5b7@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Richard Gobert and committed by
Jakub Kicinski
dff0b016 f2e3fc21

+41 -10
+41 -10
net/ipv6/ip6_offload.c
··· 37 37 INDIRECT_CALL_L4(cb, f2, f1, head, skb); \ 38 38 }) 39 39 40 + static int ipv6_gro_pull_exthdrs(struct sk_buff *skb, int off, int proto) 41 + { 42 + const struct net_offload *ops = NULL; 43 + struct ipv6_opt_hdr *opth; 44 + 45 + for (;;) { 46 + int len; 47 + 48 + ops = rcu_dereference(inet6_offloads[proto]); 49 + 50 + if (unlikely(!ops)) 51 + break; 52 + 53 + if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) 54 + break; 55 + 56 + opth = skb_gro_header(skb, off + sizeof(*opth), off); 57 + if (unlikely(!opth)) 58 + break; 59 + 60 + len = ipv6_optlen(opth); 61 + 62 + opth = skb_gro_header(skb, off + len, off); 63 + if (unlikely(!opth)) 64 + break; 65 + proto = opth->nexthdr; 66 + 67 + off += len; 68 + } 69 + 70 + skb_gro_pull(skb, off - skb_network_offset(skb)); 71 + return proto; 72 + } 73 + 40 74 static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto) 41 75 { 42 76 const struct net_offload *ops = NULL; ··· 237 203 goto out; 238 204 239 205 skb_set_network_header(skb, off); 240 - skb_gro_pull(skb, sizeof(*iph)); 241 - skb_set_transport_header(skb, skb_gro_offset(skb)); 242 206 243 - flush += ntohs(iph->payload_len) != skb_gro_len(skb); 207 + flush += ntohs(iph->payload_len) != skb->len - hlen; 244 208 245 209 proto = iph->nexthdr; 246 210 ops = rcu_dereference(inet6_offloads[proto]); 247 211 if (!ops || !ops->callbacks.gro_receive) { 248 - pskb_pull(skb, skb_gro_offset(skb)); 249 - skb_gro_frag0_invalidate(skb); 250 - proto = ipv6_gso_pull_exthdrs(skb, proto); 251 - skb_gro_pull(skb, -skb_transport_offset(skb)); 252 - skb_reset_transport_header(skb); 253 - __skb_push(skb, skb_gro_offset(skb)); 212 + proto = ipv6_gro_pull_exthdrs(skb, hlen, proto); 254 213 255 214 ops = rcu_dereference(inet6_offloads[proto]); 256 215 if (!ops || !ops->callbacks.gro_receive) 257 216 goto out; 258 217 259 - iph = ipv6_hdr(skb); 218 + iph = skb_gro_network_header(skb); 219 + } else { 220 + skb_gro_pull(skb, sizeof(*iph)); 260 221 } 222 + 223 + skb_set_transport_header(skb, skb_gro_offset(skb)); 261 224 262 225 NAPI_GRO_CB(skb)->proto = proto; 263 226