Re: [PATCH net-next 05/15] ipv6/gso: remove temporary HBH/jumbo header
From: Eric Dumazet <edumazet@google.com>
Date: 2022-02-04 01:14:58
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
On Thu, Feb 3, 2022 at 4:27 PM Eric Dumazet [off-list ref] wrote:
On Thu, Feb 3, 2022 at 4:05 PM Alexander Duyck [off-list ref] wrote:quoted
quoted
I get that. What I was getting at was that we might be able to process it in ipv6_gso_segment before we hand it off to either TCP or UDP gso handlers to segment. The general idea being we keep the IPv6 specific bits in the IPv6 specific code instead of having the skb_segment function now have to understand IPv6 packets. So what we would end up doing is having to do an skb_cow to replace the skb->head if any clones might be holding on it, and then just chop off the HBH jumbo header before we start the segmenting. The risk would be that we waste cycles removing the HBH header for a frame that is going to fail, but I am not sure how likely a scenario that is or if we need to optimize for that.I guess I can try this for the next version, thanks.
I came up with:
ommit 147f17169ccc6c2c38ea802e5728528ed54f492d
Author: Eric Dumazet [off-list ref]
Date: Sat Nov 20 16:49:35 2021 -0800
ipv6/gso: remove temporary HBH/jumbo header
ipv6 tcp and gro stacks will soon be able to build big TCP packets,
with an added temporary Hop By Hop header.
If GSO is involved for these large packets, we need to remove
the temporary HBH header before segmentation happens.
v2: perform HBH removal from ipv6_gso_segment() instead of
skb_segment() (Alexander feedback)
Signed-off-by: Eric Dumazet [off-list ref]
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index ea2a4351b654f8bc96503aae2b9adcd478e1f8b2..a850c18dae0dfedccb9d956bf1ec9fa6b0368c6b100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h@@ -464,6 +464,38 @@ bool ipv6_opt_accepted(const struct sock *sk,const struct sk_buff *skb,
struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
struct ipv6_txoptions *opt);
+/* This helper is specialized for BIG TCP needs.
+ * It assumes the hop_jumbo_hdr will immediately follow the IPV6 header.
+ * It assumes headers are already in skb->head, thus the sk argument
is only read.
+ */
+static inline bool ipv6_has_hopopt_jumbo(const struct sk_buff *skb)
+{
+ const struct hop_jumbo_hdr *jhdr;
+ const struct ipv6hdr *nhdr;
+
+ if (likely(skb->len <= GRO_MAX_SIZE))
+ return false;
+
+ if (skb->protocol != htons(ETH_P_IPV6))
+ return false;
+
+ if (skb_network_offset(skb) +
+ sizeof(struct ipv6hdr) +
+ sizeof(struct hop_jumbo_hdr) > skb_headlen(skb))
+ return false;
+
+ nhdr = ipv6_hdr(skb);
+
+ if (nhdr->nexthdr != NEXTHDR_HOP)
+ return false;
+
+ jhdr = (const struct hop_jumbo_hdr *) (nhdr + 1);
+ if (jhdr->tlv_type != IPV6_TLV_JUMBO || jhdr->hdrlen != 0 ||
+ jhdr->nexthdr != IPPROTO_TCP)
+ return false;
+ return true;
+}
+
static inline bool ipv6_accept_ra(struct inet6_dev *idev)
{
/* If forwarding is enabled, RA are not accepted unless the specialdiff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index d37a79a8554e92a1dcaa6fd023cafe2114841ece..7f65097c8f30fa19a8c9c265eb4f027e91848021100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c@@ -87,6 +87,27 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, bool gso_partial; skb_reset_network_header(skb); + if (ipv6_has_hopopt_jumbo(skb)) { + const int hophdr_len = sizeof(struct hop_jumbo_hdr); + int err; + + err = skb_cow_head(skb, 0); + if (err < 0) + return ERR_PTR(err); + + /* remove the HBH header. + * Layout: [Ethernet header][IPv6 header][HBH][TCP header] + */ + memmove(skb->data + hophdr_len, + skb->data, + ETH_HLEN + sizeof(struct ipv6hdr)); + skb->data += hophdr_len; + skb->len -= hophdr_len; + skb->network_header += hophdr_len; + skb->mac_header += hophdr_len; + ipv6h = (struct ipv6hdr *)skb->data; + ipv6h->nexthdr = IPPROTO_TCP; + } nhoff = skb_network_header(skb) - skb_mac_header(skb); if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) goto out;