|
42 | 42 | #include <linux/skbuff.h>
|
43 | 43 | #include <linux/slab.h>
|
44 | 44 | #include <linux/export.h>
|
| 45 | +#include <linux/tcp.h> |
| 46 | +#include <linux/udp.h> |
45 | 47 |
|
46 | 48 | #include <net/sock.h>
|
47 | 49 | #include <net/snmp.h>
|
@@ -322,7 +324,9 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
|
322 | 324 | struct frag_queue *fq;
|
323 | 325 | const struct ipv6hdr *hdr = ipv6_hdr(skb);
|
324 | 326 | struct net *net = dev_net(skb_dst(skb)->dev);
|
325 |
| - int iif; |
| 327 | + __be16 frag_off; |
| 328 | + int iif, offset; |
| 329 | + u8 nexthdr; |
326 | 330 |
|
327 | 331 | if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED)
|
328 | 332 | goto fail_hdr;
|
@@ -351,6 +355,33 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
|
351 | 355 | return 1;
|
352 | 356 | }
|
353 | 357 |
|
| 358 | + /* RFC 8200, Section 4.5 Fragment Header: |
| 359 | + * If the first fragment does not include all headers through an |
| 360 | + * Upper-Layer header, then that fragment should be discarded and |
| 361 | + * an ICMP Parameter Problem, Code 3, message should be sent to |
| 362 | + * the source of the fragment, with the Pointer field set to zero. |
| 363 | + */ |
| 364 | + nexthdr = hdr->nexthdr; |
| 365 | + offset = ipv6_skip_exthdr(skb, skb_transport_offset(skb), &nexthdr, &frag_off); |
| 366 | + if (offset >= 0) { |
| 367 | + /* Check some common protocols' header */ |
| 368 | + if (nexthdr == IPPROTO_TCP) |
| 369 | + offset += sizeof(struct tcphdr); |
| 370 | + else if (nexthdr == IPPROTO_UDP) |
| 371 | + offset += sizeof(struct udphdr); |
| 372 | + else if (nexthdr == IPPROTO_ICMPV6) |
| 373 | + offset += sizeof(struct icmp6hdr); |
| 374 | + else |
| 375 | + offset += 1; |
| 376 | + |
| 377 | + if (!(frag_off & htons(IP6_OFFSET)) && offset > skb->len) { |
| 378 | + __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), |
| 379 | + IPSTATS_MIB_INHDRERRORS); |
| 380 | + icmpv6_param_prob(skb, ICMPV6_HDR_INCOMP, 0); |
| 381 | + return -1; |
| 382 | + } |
| 383 | + } |
| 384 | + |
354 | 385 | iif = skb->dev ? skb->dev->ifindex : 0;
|
355 | 386 | fq = fq_find(net, fhdr->identification, hdr, iif);
|
356 | 387 | if (fq) {
|
|
0 commit comments