.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * IPv6 fragment reassembly |
---|
3 | 4 | * Linux INET6 implementation |
---|
.. | .. |
---|
6 | 7 | * Pedro Roque <roque@di.fc.ul.pt> |
---|
7 | 8 | * |
---|
8 | 9 | * Based on: net/ipv4/ip_fragment.c |
---|
9 | | - * |
---|
10 | | - * This program is free software; you can redistribute it and/or |
---|
11 | | - * modify it under the terms of the GNU General Public License |
---|
12 | | - * as published by the Free Software Foundation; either version |
---|
13 | | - * 2 of the License, or (at your option) any later version. |
---|
14 | 10 | */ |
---|
15 | 11 | |
---|
16 | 12 | /* |
---|
.. | .. |
---|
46 | 42 | #include <linux/skbuff.h> |
---|
47 | 43 | #include <linux/slab.h> |
---|
48 | 44 | #include <linux/export.h> |
---|
| 45 | +#include <linux/tcp.h> |
---|
| 46 | +#include <linux/udp.h> |
---|
49 | 47 | |
---|
50 | 48 | #include <net/sock.h> |
---|
51 | 49 | #include <net/snmp.h> |
---|
.. | .. |
---|
76 | 74 | { |
---|
77 | 75 | struct inet_frag_queue *frag = from_timer(frag, t, timer); |
---|
78 | 76 | struct frag_queue *fq; |
---|
79 | | - struct net *net; |
---|
80 | 77 | |
---|
81 | 78 | fq = container_of(frag, struct frag_queue, q); |
---|
82 | | - net = container_of(fq->q.net, struct net, ipv6.frags); |
---|
83 | 79 | |
---|
84 | | - ip6frag_expire_frag_queue(net, fq); |
---|
| 80 | + ip6frag_expire_frag_queue(fq->q.fqdir->net, fq); |
---|
85 | 81 | } |
---|
86 | 82 | |
---|
87 | 83 | static struct frag_queue * |
---|
.. | .. |
---|
100 | 96 | IPV6_ADDR_LINKLOCAL))) |
---|
101 | 97 | key.iif = 0; |
---|
102 | 98 | |
---|
103 | | - q = inet_frag_find(&net->ipv6.frags, &key); |
---|
| 99 | + q = inet_frag_find(net->ipv6.fqdir, &key); |
---|
104 | 100 | if (!q) |
---|
105 | 101 | return NULL; |
---|
106 | 102 | |
---|
.. | .. |
---|
200 | 196 | fq->q.stamp = skb->tstamp; |
---|
201 | 197 | fq->q.meat += skb->len; |
---|
202 | 198 | fq->ecn |= ecn; |
---|
203 | | - add_frag_mem_limit(fq->q.net, skb->truesize); |
---|
| 199 | + add_frag_mem_limit(fq->q.fqdir, skb->truesize); |
---|
204 | 200 | |
---|
205 | 201 | fragsize = -skb_network_offset(skb) + skb->len; |
---|
206 | 202 | if (fragsize > fq->q.max_size) |
---|
.. | .. |
---|
254 | 250 | static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb, |
---|
255 | 251 | struct sk_buff *prev_tail, struct net_device *dev) |
---|
256 | 252 | { |
---|
257 | | - struct net *net = container_of(fq->q.net, struct net, ipv6.frags); |
---|
| 253 | + struct net *net = fq->q.fqdir->net; |
---|
258 | 254 | unsigned int nhoff; |
---|
259 | 255 | void *reasm_data; |
---|
260 | 256 | int payload_len; |
---|
.. | .. |
---|
288 | 284 | |
---|
289 | 285 | skb_reset_transport_header(skb); |
---|
290 | 286 | |
---|
291 | | - inet_frag_reasm_finish(&fq->q, skb, reasm_data); |
---|
| 287 | + inet_frag_reasm_finish(&fq->q, skb, reasm_data, true); |
---|
292 | 288 | |
---|
293 | 289 | skb->dev = dev; |
---|
294 | 290 | ipv6_hdr(skb)->payload_len = htons(payload_len); |
---|
.. | .. |
---|
302 | 298 | skb_network_header_len(skb)); |
---|
303 | 299 | |
---|
304 | 300 | rcu_read_lock(); |
---|
305 | | - __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS); |
---|
| 301 | + __IP6_INC_STATS(net, __in6_dev_stats_get(dev, skb), IPSTATS_MIB_REASMOKS); |
---|
306 | 302 | rcu_read_unlock(); |
---|
307 | | - fq->q.fragments = NULL; |
---|
308 | 303 | fq->q.rb_fragments = RB_ROOT; |
---|
309 | 304 | fq->q.fragments_tail = NULL; |
---|
310 | 305 | fq->q.last_run_head = NULL; |
---|
.. | .. |
---|
317 | 312 | net_dbg_ratelimited("ip6_frag_reasm: no memory for reassembly\n"); |
---|
318 | 313 | out_fail: |
---|
319 | 314 | rcu_read_lock(); |
---|
320 | | - __IP6_INC_STATS(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); |
---|
| 315 | + __IP6_INC_STATS(net, __in6_dev_stats_get(dev, skb), IPSTATS_MIB_REASMFAILS); |
---|
321 | 316 | rcu_read_unlock(); |
---|
322 | 317 | inet_frag_kill(&fq->q); |
---|
323 | 318 | return -1; |
---|
.. | .. |
---|
329 | 324 | struct frag_queue *fq; |
---|
330 | 325 | const struct ipv6hdr *hdr = ipv6_hdr(skb); |
---|
331 | 326 | struct net *net = dev_net(skb_dst(skb)->dev); |
---|
| 327 | + u8 nexthdr; |
---|
332 | 328 | int iif; |
---|
333 | 329 | |
---|
334 | 330 | if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED) |
---|
.. | .. |
---|
358 | 354 | IP6CB(skb)->frag_max_size = ntohs(hdr->payload_len) + |
---|
359 | 355 | sizeof(struct ipv6hdr); |
---|
360 | 356 | return 1; |
---|
| 357 | + } |
---|
| 358 | + |
---|
| 359 | + /* RFC 8200, Section 4.5 Fragment Header: |
---|
| 360 | + * If the first fragment does not include all headers through an |
---|
| 361 | + * Upper-Layer header, then that fragment should be discarded and |
---|
| 362 | + * an ICMP Parameter Problem, Code 3, message should be sent to |
---|
| 363 | + * the source of the fragment, with the Pointer field set to zero. |
---|
| 364 | + */ |
---|
| 365 | + nexthdr = hdr->nexthdr; |
---|
| 366 | + if (ipv6frag_thdr_truncated(skb, skb_transport_offset(skb), &nexthdr)) { |
---|
| 367 | + __IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev), |
---|
| 368 | + IPSTATS_MIB_INHDRERRORS); |
---|
| 369 | + icmpv6_param_prob(skb, ICMPV6_HDR_INCOMP, 0); |
---|
| 370 | + return -1; |
---|
361 | 371 | } |
---|
362 | 372 | |
---|
363 | 373 | iif = skb->dev ? skb->dev->ifindex : 0; |
---|
.. | .. |
---|
404 | 414 | static struct ctl_table ip6_frags_ns_ctl_table[] = { |
---|
405 | 415 | { |
---|
406 | 416 | .procname = "ip6frag_high_thresh", |
---|
407 | | - .data = &init_net.ipv6.frags.high_thresh, |
---|
408 | 417 | .maxlen = sizeof(unsigned long), |
---|
409 | 418 | .mode = 0644, |
---|
410 | 419 | .proc_handler = proc_doulongvec_minmax, |
---|
411 | | - .extra1 = &init_net.ipv6.frags.low_thresh |
---|
412 | 420 | }, |
---|
413 | 421 | { |
---|
414 | 422 | .procname = "ip6frag_low_thresh", |
---|
415 | | - .data = &init_net.ipv6.frags.low_thresh, |
---|
416 | 423 | .maxlen = sizeof(unsigned long), |
---|
417 | 424 | .mode = 0644, |
---|
418 | 425 | .proc_handler = proc_doulongvec_minmax, |
---|
419 | | - .extra2 = &init_net.ipv6.frags.high_thresh |
---|
420 | 426 | }, |
---|
421 | 427 | { |
---|
422 | 428 | .procname = "ip6frag_time", |
---|
423 | | - .data = &init_net.ipv6.frags.timeout, |
---|
424 | 429 | .maxlen = sizeof(int), |
---|
425 | 430 | .mode = 0644, |
---|
426 | 431 | .proc_handler = proc_dointvec_jiffies, |
---|
.. | .. |
---|
452 | 457 | if (!table) |
---|
453 | 458 | goto err_alloc; |
---|
454 | 459 | |
---|
455 | | - table[0].data = &net->ipv6.frags.high_thresh; |
---|
456 | | - table[0].extra1 = &net->ipv6.frags.low_thresh; |
---|
457 | | - table[0].extra2 = &init_net.ipv6.frags.high_thresh; |
---|
458 | | - table[1].data = &net->ipv6.frags.low_thresh; |
---|
459 | | - table[1].extra2 = &net->ipv6.frags.high_thresh; |
---|
460 | | - table[2].data = &net->ipv6.frags.timeout; |
---|
461 | 460 | } |
---|
| 461 | + table[0].data = &net->ipv6.fqdir->high_thresh; |
---|
| 462 | + table[0].extra1 = &net->ipv6.fqdir->low_thresh; |
---|
| 463 | + table[1].data = &net->ipv6.fqdir->low_thresh; |
---|
| 464 | + table[1].extra2 = &net->ipv6.fqdir->high_thresh; |
---|
| 465 | + table[2].data = &net->ipv6.fqdir->timeout; |
---|
462 | 466 | |
---|
463 | 467 | hdr = register_net_sysctl(net, "net/ipv6", table); |
---|
464 | 468 | if (!hdr) |
---|
.. | .. |
---|
521 | 525 | { |
---|
522 | 526 | int res; |
---|
523 | 527 | |
---|
524 | | - net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; |
---|
525 | | - net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH; |
---|
526 | | - net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; |
---|
527 | | - net->ipv6.frags.f = &ip6_frags; |
---|
528 | | - |
---|
529 | | - res = inet_frags_init_net(&net->ipv6.frags); |
---|
| 528 | + res = fqdir_init(&net->ipv6.fqdir, &ip6_frags, net); |
---|
530 | 529 | if (res < 0) |
---|
531 | 530 | return res; |
---|
532 | 531 | |
---|
| 532 | + net->ipv6.fqdir->high_thresh = IPV6_FRAG_HIGH_THRESH; |
---|
| 533 | + net->ipv6.fqdir->low_thresh = IPV6_FRAG_LOW_THRESH; |
---|
| 534 | + net->ipv6.fqdir->timeout = IPV6_FRAG_TIMEOUT; |
---|
| 535 | + |
---|
533 | 536 | res = ip6_frags_ns_sysctl_register(net); |
---|
534 | 537 | if (res < 0) |
---|
535 | | - inet_frags_exit_net(&net->ipv6.frags); |
---|
| 538 | + fqdir_exit(net->ipv6.fqdir); |
---|
536 | 539 | return res; |
---|
| 540 | +} |
---|
| 541 | + |
---|
| 542 | +static void __net_exit ipv6_frags_pre_exit_net(struct net *net) |
---|
| 543 | +{ |
---|
| 544 | + fqdir_pre_exit(net->ipv6.fqdir); |
---|
537 | 545 | } |
---|
538 | 546 | |
---|
539 | 547 | static void __net_exit ipv6_frags_exit_net(struct net *net) |
---|
540 | 548 | { |
---|
541 | 549 | ip6_frags_ns_sysctl_unregister(net); |
---|
542 | | - inet_frags_exit_net(&net->ipv6.frags); |
---|
| 550 | + fqdir_exit(net->ipv6.fqdir); |
---|
543 | 551 | } |
---|
544 | 552 | |
---|
545 | 553 | static struct pernet_operations ip6_frags_ops = { |
---|
546 | | - .init = ipv6_frags_init_net, |
---|
547 | | - .exit = ipv6_frags_exit_net, |
---|
| 554 | + .init = ipv6_frags_init_net, |
---|
| 555 | + .pre_exit = ipv6_frags_pre_exit_net, |
---|
| 556 | + .exit = ipv6_frags_exit_net, |
---|
548 | 557 | }; |
---|
549 | 558 | |
---|
550 | 559 | static const struct rhashtable_params ip6_rhash_params = { |
---|