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
 
        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;
        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;