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 <edumazet@google.com> Acked-by: Alexander Duyck <alexanderduyck@fb.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
7c96d8ec96
commit
09f3d1a3a5
|
@ -467,6 +467,39 @@ 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.
|
||||
* Returns 0, or IPPROTO_TCP if a BIG TCP packet is there.
|
||||
*/
|
||||
static inline int 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 0;
|
||||
|
||||
if (skb->protocol != htons(ETH_P_IPV6))
|
||||
return 0;
|
||||
|
||||
if (skb_network_offset(skb) +
|
||||
sizeof(struct ipv6hdr) +
|
||||
sizeof(struct hop_jumbo_hdr) > skb_headlen(skb))
|
||||
return 0;
|
||||
|
||||
nhdr = ipv6_hdr(skb);
|
||||
|
||||
if (nhdr->nexthdr != NEXTHDR_HOP)
|
||||
return 0;
|
||||
|
||||
jhdr = (const struct hop_jumbo_hdr *) (nhdr + 1);
|
||||
if (jhdr->tlv_type != IPV6_TLV_JUMBO || jhdr->hdrlen != 0 ||
|
||||
jhdr->nexthdr != IPPROTO_TCP)
|
||||
return 0;
|
||||
return jhdr->nexthdr;
|
||||
}
|
||||
|
||||
static inline bool ipv6_accept_ra(struct inet6_dev *idev)
|
||||
{
|
||||
/* If forwarding is enabled, RA are not accepted unless the special
|
||||
|
|
|
@ -77,7 +77,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
|
|||
struct sk_buff *segs = ERR_PTR(-EINVAL);
|
||||
struct ipv6hdr *ipv6h;
|
||||
const struct net_offload *ops;
|
||||
int proto;
|
||||
int proto, nexthdr;
|
||||
struct frag_hdr *fptr;
|
||||
unsigned int payload_len;
|
||||
u8 *prevhdr;
|
||||
|
@ -87,6 +87,28 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
|
|||
bool gso_partial;
|
||||
|
||||
skb_reset_network_header(skb);
|
||||
nexthdr = ipv6_has_hopopt_jumbo(skb);
|
||||
if (nexthdr) {
|
||||
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_mac_header(skb) + hophdr_len,
|
||||
skb_mac_header(skb),
|
||||
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 = nexthdr;
|
||||
}
|
||||
nhoff = skb_network_header(skb) - skb_mac_header(skb);
|
||||
if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
|
||||
goto out;
|
||||
|
|
Loading…
Reference in New Issue