int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
 {
        u16 savethdr = skb->transport_header;
+       u8 nexthdr = NEXTHDR_FRAGMENT;
        int fhoff, nhoff, ret;
        struct frag_hdr *fhdr;
        struct frag_queue *fq;
        if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
                return 0;
 
+       /* Discard the first fragment if it does not include all headers
+        * RFC 8200, Section 4.5
+        */
+       if (ipv6_frag_thdr_truncated(skb, fhoff, &nexthdr)) {
+               pr_debug("Drop incomplete fragment\n");
+               return 0;
+       }
+
        if (!pskb_may_pull(skb, fhoff + sizeof(*fhdr)))
                return -ENOMEM;
 
 
        return -1;
 }
 
+/* Check if the upper layer header is truncated in the first fragment. */
+bool ipv6_frag_thdr_truncated(struct sk_buff *skb, int start, u8 *nexthdrp)
+{
+       u8 nexthdr = *nexthdrp;
+       __be16 frag_off;
+       int offset;
+
+       offset = ipv6_skip_exthdr(skb, start, &nexthdr, &frag_off);
+       if (offset < 0 || (frag_off & htons(IP6_OFFSET)))
+               return false;
+       switch (nexthdr) {
+       case NEXTHDR_TCP:
+               offset += sizeof(struct tcphdr);
+               break;
+       case NEXTHDR_UDP:
+               offset += sizeof(struct udphdr);
+               break;
+       case NEXTHDR_ICMP:
+               offset += sizeof(struct icmp6hdr);
+               break;
+       default:
+               offset += 1;
+       }
+       if (offset > skb->len)
+               return true;
+       return false;
+}
+EXPORT_SYMBOL(ipv6_frag_thdr_truncated);
+
 static int ipv6_frag_rcv(struct sk_buff *skb)
 {
        struct frag_hdr *fhdr;
        struct frag_queue *fq;
        const struct ipv6hdr *hdr = ipv6_hdr(skb);
        struct net *net = dev_net(skb_dst(skb)->dev);
-       __be16 frag_off;
-       int iif, offset;
        u8 nexthdr;
+       int iif;
 
        if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED)
                goto fail_hdr;
         * the source of the fragment, with the Pointer field set to zero.
         */
        nexthdr = hdr->nexthdr;
-       offset = ipv6_skip_exthdr(skb, skb_transport_offset(skb), &nexthdr, &frag_off);
-       if (offset >= 0) {
-               /* Check some common protocols' header */
-               if (nexthdr == IPPROTO_TCP)
-                       offset += sizeof(struct tcphdr);
-               else if (nexthdr == IPPROTO_UDP)
-                       offset += sizeof(struct udphdr);
-               else if (nexthdr == IPPROTO_ICMPV6)
-                       offset += sizeof(struct icmp6hdr);
-               else
-                       offset += 1;
-
-               if (!(frag_off & htons(IP6_OFFSET)) && offset > skb->len) {
-                       __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev),
-                                       IPSTATS_MIB_INHDRERRORS);
-                       icmpv6_param_prob(skb, ICMPV6_HDR_INCOMP, 0);
-                       return -1;
-               }
+       if (ipv6_frag_thdr_truncated(skb, skb_transport_offset(skb), &nexthdr)) {
+               __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev),
+                               IPSTATS_MIB_INHDRERRORS);
+               icmpv6_param_prob(skb, ICMPV6_HDR_INCOMP, 0);
+               return -1;
        }
 
        iif = skb->dev ? skb->dev->ifindex : 0;