1 /*
2 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* $FreeBSD: src/sys/netinet6/frag6.c,v 1.2.2.5 2001/07/03 11:01:50 ume Exp $ */
30 /* $KAME: frag6.c,v 1.31 2001/05/17 13:45:34 jinmei Exp $ */
31
32 /*
33 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the project nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/malloc.h>
64 #include <sys/mcache.h>
65 #include <sys/mbuf.h>
66 #include <sys/domain.h>
67 #include <sys/protosw.h>
68 #include <sys/socket.h>
69 #include <sys/errno.h>
70 #include <sys/time.h>
71 #include <sys/kernel.h>
72 #include <sys/syslog.h>
73 #include <kern/queue.h>
74 #include <kern/locks.h>
75
76 #include <net/if.h>
77 #include <net/route.h>
78
79 #include <netinet/in.h>
80 #include <netinet/in_var.h>
81 #include <netinet/ip.h>
82 #include <netinet/ip_var.h>
83 #include <netinet/ip6.h>
84 #include <netinet6/ip6_var.h>
85 #include <netinet/icmp6.h>
86
87 #include <net/net_osdep.h>
88 #include <dev/random/randomdev.h>
89
90 /*
91 * Define it to get a correct behavior on per-interface statistics.
92 */
93 #define IN6_IFSTAT_STRICT
94 struct ip6asfrag {
95 struct ip6asfrag *ip6af_down;
96 struct ip6asfrag *ip6af_up;
97 struct mbuf *ip6af_m;
98 int ip6af_offset; /* offset in ip6af_m to next header */
99 int ip6af_frglen; /* fragmentable part length */
100 int ip6af_off; /* fragment offset */
101 u_int16_t ip6af_mff; /* more fragment bit in frag off */
102 };
103
104 #define IP6_REASS_MBUF(ip6af) ((ip6af)->ip6af_m)
105
106 MBUFQ_HEAD(fq6_head);
107
108 static void frag6_save_context(struct mbuf *, int);
109 static void frag6_scrub_context(struct mbuf *);
110 static int frag6_restore_context(struct mbuf *);
111
112 static void frag6_icmp6_paramprob_error(struct fq6_head *);
113 static void frag6_icmp6_timeex_error(struct fq6_head *);
114
115 static void frag6_enq(struct ip6asfrag *, struct ip6asfrag *);
116 static void frag6_deq(struct ip6asfrag *);
117 static void frag6_insque(struct ip6q *, struct ip6q *);
118 static void frag6_remque(struct ip6q *);
119 static void frag6_purgef(struct ip6q *, struct fq6_head *, struct fq6_head *);
120 static void frag6_freef(struct ip6q *, struct fq6_head *, struct fq6_head *);
121
122 static int frag6_timeout_run; /* frag6 timer is scheduled to run */
123 static void frag6_timeout(void *);
124 static void frag6_sched_timeout(void);
125
126 static struct ip6q *ip6q_alloc(int);
127 static void ip6q_free(struct ip6q *);
128 static void ip6q_updateparams(void);
129 static struct ip6asfrag *ip6af_alloc(int);
130 static void ip6af_free(struct ip6asfrag *);
131
132 static LCK_GRP_DECLARE(ip6qlock_grp, "ip6qlock");
133 static LCK_MTX_DECLARE(ip6qlock, &ip6qlock_grp);
134
135 /* IPv6 fragment reassembly queues (protected by ip6qlock) */
136 static struct ip6q ip6q; /* ip6 reassembly queues */
137 static int ip6_maxfragpackets; /* max packets in reass queues */
138 static u_int32_t frag6_nfragpackets; /* # of packets in reass queues */
139 static int ip6_maxfrags; /* max fragments in reass queues */
140 static u_int32_t frag6_nfrags; /* # of fragments in reass queues */
141 static u_int32_t ip6q_limit; /* ip6q allocation limit */
142 static u_int32_t ip6q_count; /* current # of allocated ip6q's */
143 static u_int32_t ip6af_limit; /* ip6asfrag allocation limit */
144 static u_int32_t ip6af_count; /* current # of allocated ip6asfrag's */
145
146 static int sysctl_maxfragpackets SYSCTL_HANDLER_ARGS;
147 static int sysctl_maxfrags SYSCTL_HANDLER_ARGS;
148
149 SYSCTL_DECL(_net_inet6_ip6);
150
151 SYSCTL_PROC(_net_inet6_ip6, IPV6CTL_MAXFRAGPACKETS, maxfragpackets,
152 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &ip6_maxfragpackets, 0,
153 sysctl_maxfragpackets, "I",
154 "Maximum number of IPv6 fragment reassembly queue entries");
155
156 SYSCTL_UINT(_net_inet6_ip6, OID_AUTO, fragpackets,
157 CTLFLAG_RD | CTLFLAG_LOCKED, &frag6_nfragpackets, 0,
158 "Current number of IPv6 fragment reassembly queue entries");
159
160 SYSCTL_PROC(_net_inet6_ip6, IPV6CTL_MAXFRAGS, maxfrags,
161 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &ip6_maxfrags, 0,
162 sysctl_maxfrags, "I", "Maximum number of IPv6 fragments allowed");
163
164 /*
165 * Initialise reassembly queue and fragment identifier.
166 */
167 void
frag6_init(void)168 frag6_init(void)
169 {
170 /* ip6q_alloc() uses mbufs for IPv6 fragment queue structures */
171 _CASSERT(sizeof(struct ip6q) <= _MLEN);
172 /* ip6af_alloc() uses mbufs for IPv6 fragment queue structures */
173 _CASSERT(sizeof(struct ip6asfrag) <= _MLEN);
174
175 lck_mtx_lock(&ip6qlock);
176 /* Initialize IPv6 reassembly queue. */
177 ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
178
179 /* same limits as IPv4 */
180 ip6_maxfragpackets = nmbclusters / 32;
181 ip6_maxfrags = ip6_maxfragpackets * 2;
182 ip6q_updateparams();
183 lck_mtx_unlock(&ip6qlock);
184 }
185
186 static void
frag6_save_context(struct mbuf * m,int val)187 frag6_save_context(struct mbuf *m, int val)
188 {
189 m->m_pkthdr.pkt_hdr = (void *)(uintptr_t)val;
190 }
191
192 static void
frag6_scrub_context(struct mbuf * m)193 frag6_scrub_context(struct mbuf *m)
194 {
195 m->m_pkthdr.pkt_hdr = NULL;
196 }
197
198 static int
frag6_restore_context(struct mbuf * m)199 frag6_restore_context(struct mbuf *m)
200 {
201 return (int)m->m_pkthdr.pkt_hdr;
202 }
203
204 /*
205 * Send any deferred ICMP param problem error messages; caller must not be
206 * holding ip6qlock and is expected to have saved the per-packet parameter
207 * value via frag6_save_context().
208 */
209 static void
frag6_icmp6_paramprob_error(struct fq6_head * diq6)210 frag6_icmp6_paramprob_error(struct fq6_head *diq6)
211 {
212 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED);
213
214 if (!MBUFQ_EMPTY(diq6)) {
215 struct mbuf *merr, *merr_tmp;
216 int param;
217 MBUFQ_FOREACH_SAFE(merr, diq6, merr_tmp) {
218 MBUFQ_REMOVE(diq6, merr);
219 MBUFQ_NEXT(merr) = NULL;
220 param = frag6_restore_context(merr);
221 frag6_scrub_context(merr);
222 icmp6_error(merr, ICMP6_PARAM_PROB,
223 ICMP6_PARAMPROB_HEADER, param);
224 }
225 }
226 }
227
228 /*
229 * Send any deferred ICMP time exceeded error messages;
230 * caller must not be holding ip6qlock.
231 */
232 static void
frag6_icmp6_timeex_error(struct fq6_head * diq6)233 frag6_icmp6_timeex_error(struct fq6_head *diq6)
234 {
235 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED);
236
237 if (!MBUFQ_EMPTY(diq6)) {
238 struct mbuf *m, *m_tmp;
239 MBUFQ_FOREACH_SAFE(m, diq6, m_tmp) {
240 MBUFQ_REMOVE(diq6, m);
241 MBUFQ_NEXT(m) = NULL;
242 icmp6_error_flag(m, ICMP6_TIME_EXCEEDED,
243 ICMP6_TIME_EXCEED_REASSEMBLY, 0, 0);
244 }
245 }
246 }
247
248 /*
249 * In RFC2460, fragment and reassembly rule do not agree with each other,
250 * in terms of next header field handling in fragment header.
251 * While the sender will use the same value for all of the fragmented packets,
252 * receiver is suggested not to check the consistency.
253 *
254 * fragment rule (p20):
255 * (2) A Fragment header containing:
256 * The Next Header value that identifies the first header of
257 * the Fragmentable Part of the original packet.
258 * -> next header field is same for all fragments
259 *
260 * reassembly rule (p21):
261 * The Next Header field of the last header of the Unfragmentable
262 * Part is obtained from the Next Header field of the first
263 * fragment's Fragment header.
264 * -> should grab it from the first fragment only
265 *
266 * The following note also contradicts with fragment rule - noone is going to
267 * send different fragment with different next header field.
268 *
269 * additional note (p22):
270 * The Next Header values in the Fragment headers of different
271 * fragments of the same original packet may differ. Only the value
272 * from the Offset zero fragment packet is used for reassembly.
273 * -> should grab it from the first fragment only
274 *
275 * There is no explicit reason given in the RFC. Historical reason maybe?
276 */
277 /*
278 * Fragment input
279 */
280 int
frag6_input(struct mbuf ** mp,int * offp,int proto)281 frag6_input(struct mbuf **mp, int *offp, int proto)
282 {
283 #pragma unused(proto)
284 struct mbuf *m = *mp, *t = NULL;
285 struct ip6_hdr *ip6 = NULL;
286 struct ip6_frag *ip6f = NULL;
287 struct ip6q *q6 = NULL;
288 struct ip6asfrag *af6 = NULL, *ip6af = NULL, *af6dwn = NULL;
289 int offset = *offp, i = 0, next = 0;
290 u_int8_t nxt = 0;
291 int first_frag = 0;
292 int fragoff = 0, frgpartlen = 0; /* must be larger than u_int16_t */
293 struct ifnet *dstifp = NULL;
294 u_int8_t ecn = 0, ecn0 = 0;
295 uint32_t csum = 0, csum_flags = 0;
296 struct fq6_head diq6 = {};
297 int locked = 0;
298 boolean_t drop_fragq = FALSE;
299
300 VERIFY(m->m_flags & M_PKTHDR);
301
302 MBUFQ_INIT(&diq6); /* for deferred ICMP param problem errors */
303
304 /* Expect 32-bit aligned data pointer on strict-align platforms */
305 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
306
307 IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), goto done);
308 ip6 = mtod(m, struct ip6_hdr *);
309 ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset);
310
311 #ifdef IN6_IFSTAT_STRICT
312 /* find the destination interface of the packet. */
313 if (m->m_pkthdr.pkt_flags & PKTF_IFAINFO) {
314 uint32_t idx;
315
316 if (ip6_getdstifaddr_info(m, &idx, NULL) == 0) {
317 if (idx > 0 && idx <= if_index) {
318 ifnet_head_lock_shared();
319 dstifp = ifindex2ifnet[idx];
320 ifnet_head_done();
321 }
322 }
323 }
324 #endif /* IN6_IFSTAT_STRICT */
325
326 /* we are violating the spec, this may not be the dst interface */
327 if (dstifp == NULL) {
328 dstifp = m->m_pkthdr.rcvif;
329 }
330
331 /* jumbo payload can't contain a fragment header */
332 if (ip6->ip6_plen == 0) {
333 icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset);
334 in6_ifstat_inc(dstifp, ifs6_reass_fail);
335 m = NULL;
336 goto done;
337 }
338
339 /*
340 * check whether fragment packet's fragment length is
341 * multiple of 8 octets.
342 * sizeof(struct ip6_frag) == 8
343 * sizeof(struct ip6_hdr) = 40
344 */
345 if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) &&
346 (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) {
347 icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
348 offsetof(struct ip6_hdr, ip6_plen));
349 in6_ifstat_inc(dstifp, ifs6_reass_fail);
350 m = NULL;
351 goto done;
352 }
353
354 /* If ip6_maxfragpackets or ip6_maxfrags is 0, never accept fragments */
355 if (ip6_maxfragpackets == 0 || ip6_maxfrags == 0) {
356 ip6stat.ip6s_fragments++;
357 ip6stat.ip6s_fragdropped++;
358 in6_ifstat_inc(dstifp, ifs6_reass_fail);
359 m_freem(m);
360 m = NULL;
361 goto done;
362 }
363
364 /* offset now points to data portion */
365 offset += sizeof(struct ip6_frag);
366
367 /*
368 * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0)
369 * upfront, unrelated to any reassembly. Just skip the fragment header.
370 */
371 if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) {
372 /*
373 * Mark packet as reassembled.
374 * In ICMPv6 processing, we drop certain
375 * NDP messages that are not expected to
376 * have fragment header based on recommendations
377 * against security vulnerability as described in
378 * RFC 6980.
379 * Treat atomic fragments as re-assembled packets as well.
380 */
381 m->m_pkthdr.pkt_flags |= PKTF_REASSEMBLED;
382 ip6stat.ip6s_atmfrag_rcvd++;
383 in6_ifstat_inc(dstifp, ifs6_atmfrag_rcvd);
384 *mp = m;
385 *offp = offset;
386 return ip6f->ip6f_nxt;
387 }
388
389 /*
390 * Leverage partial checksum offload for simple UDP/IP fragments,
391 * as that is the most common case.
392 *
393 * Perform 1's complement adjustment of octets that got included/
394 * excluded in the hardware-calculated checksum value. Also take
395 * care of any trailing bytes and subtract out their partial sum.
396 */
397 if (ip6f->ip6f_nxt == IPPROTO_UDP &&
398 offset == (sizeof(*ip6) + sizeof(*ip6f)) &&
399 (m->m_pkthdr.csum_flags &
400 (CSUM_DATA_VALID | CSUM_PARTIAL | CSUM_PSEUDO_HDR)) ==
401 (CSUM_DATA_VALID | CSUM_PARTIAL)) {
402 uint32_t start = m->m_pkthdr.csum_rx_start;
403 uint32_t ip_len = (sizeof(*ip6) + ntohs(ip6->ip6_plen));
404 int32_t trailer = (m_pktlen(m) - ip_len);
405 uint32_t swbytes = (uint32_t)trailer;
406
407 csum = m->m_pkthdr.csum_rx_val;
408
409 ASSERT(trailer >= 0);
410 if (start != offset || trailer != 0) {
411 uint16_t s = 0, d = 0;
412
413 if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) {
414 s = ip6->ip6_src.s6_addr16[1];
415 ip6->ip6_src.s6_addr16[1] = 0;
416 }
417 if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) {
418 d = ip6->ip6_dst.s6_addr16[1];
419 ip6->ip6_dst.s6_addr16[1] = 0;
420 }
421
422 /* callee folds in sum */
423 csum = m_adj_sum16(m, start, offset,
424 (ip_len - offset), csum);
425 if (offset > start) {
426 swbytes += (offset - start);
427 } else {
428 swbytes += (start - offset);
429 }
430
431 if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) {
432 ip6->ip6_src.s6_addr16[1] = s;
433 }
434 if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) {
435 ip6->ip6_dst.s6_addr16[1] = d;
436 }
437 }
438 csum_flags = m->m_pkthdr.csum_flags;
439
440 if (swbytes != 0) {
441 udp_in6_cksum_stats(swbytes);
442 }
443 if (trailer != 0) {
444 m_adj(m, -trailer);
445 }
446 } else {
447 csum = 0;
448 csum_flags = 0;
449 }
450
451 /* Invalidate checksum */
452 m->m_pkthdr.csum_flags &= ~CSUM_DATA_VALID;
453
454 ip6stat.ip6s_fragments++;
455 in6_ifstat_inc(dstifp, ifs6_reass_reqd);
456
457 lck_mtx_lock(&ip6qlock);
458 locked = 1;
459
460 for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) {
461 if (ip6f->ip6f_ident == q6->ip6q_ident &&
462 in6_are_addr_equal_scoped(&ip6->ip6_src, &q6->ip6q_src, ip6_input_getsrcifscope(m), q6->ip6q_src_ifscope) &&
463 in6_are_addr_equal_scoped(&ip6->ip6_dst, &q6->ip6q_dst, ip6_input_getdstifscope(m), q6->ip6q_dst_ifscope)) {
464 break;
465 }
466 }
467
468 if (q6 == &ip6q) {
469 /*
470 * Create a reassembly queue as this is the first fragment to
471 * arrive.
472 * By first frag, we don't mean the one with offset 0, but
473 * any of the fragments of the fragmented packet that has
474 * reached us first.
475 */
476 first_frag = 1;
477
478 q6 = ip6q_alloc(M_DONTWAIT);
479 if (q6 == NULL) {
480 goto dropfrag;
481 }
482
483 frag6_insque(q6, &ip6q);
484 frag6_nfragpackets++;
485
486 /* ip6q_nxt will be filled afterwards, from 1st fragment */
487 q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6;
488 #ifdef notyet
489 q6->ip6q_nxtp = (u_char *)nxtp;
490 #endif
491 q6->ip6q_ident = ip6f->ip6f_ident;
492 q6->ip6q_ttl = IPV6_FRAGTTL;
493 q6->ip6q_src = ip6->ip6_src;
494 q6->ip6q_dst = ip6->ip6_dst;
495 q6->ip6q_dst_ifscope = IN6_IS_SCOPE_EMBED(&q6->ip6q_dst) ? ip6_input_getdstifscope(m) : IFSCOPE_NONE;
496 q6->ip6q_src_ifscope = IN6_IS_SCOPE_EMBED(&q6->ip6q_src) ? ip6_input_getsrcifscope(m) : IFSCOPE_NONE;
497 q6->ip6q_ecn =
498 (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK;
499 q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */
500
501 q6->ip6q_nfrag = 0;
502 q6->ip6q_flags = 0;
503
504 /*
505 * If the first fragment has valid checksum offload
506 * info, the rest of fragments are eligible as well.
507 */
508 if (csum_flags != 0) {
509 q6->ip6q_csum = csum;
510 q6->ip6q_csum_flags = csum_flags;
511 }
512 }
513
514 if (q6->ip6q_flags & IP6QF_DIRTY) {
515 goto dropfrag;
516 }
517
518 /*
519 * If it's the 1st fragment, record the length of the
520 * unfragmentable part and the next header of the fragment header.
521 */
522 fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
523 if (fragoff == 0) {
524 q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) -
525 sizeof(struct ip6_frag);
526 q6->ip6q_nxt = ip6f->ip6f_nxt;
527 }
528
529 /*
530 * Check that the reassembled packet would not exceed 65535 bytes
531 * in size.
532 * If it would exceed, discard the fragment and return an ICMP error.
533 */
534 frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset;
535 if (q6->ip6q_unfrglen >= 0) {
536 /* The 1st fragment has already arrived. */
537 if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) {
538 lck_mtx_unlock(&ip6qlock);
539 locked = 0;
540 icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
541 offset - sizeof(struct ip6_frag) +
542 offsetof(struct ip6_frag, ip6f_offlg));
543 m = NULL;
544 goto done;
545 }
546 } else if (fragoff + frgpartlen > IPV6_MAXPACKET) {
547 lck_mtx_unlock(&ip6qlock);
548 locked = 0;
549 icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
550 offset - sizeof(struct ip6_frag) +
551 offsetof(struct ip6_frag, ip6f_offlg));
552 m = NULL;
553 goto done;
554 }
555 /*
556 * If it's the first fragment, do the above check for each
557 * fragment already stored in the reassembly queue.
558 */
559 if (fragoff == 0) {
560 /*
561 * https://tools.ietf.org/html/rfc8200#page-20
562 * If the first fragment does not include all headers through an
563 * Upper-Layer header, then that fragment should be discarded and
564 * an ICMP Parameter Problem, Code 3, message should be sent to
565 * the source of the fragment, with the Pointer field set to zero.
566 */
567 if (!ip6_pkt_has_ulp(m)) {
568 lck_mtx_unlock(&ip6qlock);
569 locked = 0;
570 icmp6_error(m, ICMP6_PARAM_PROB,
571 ICMP6_PARAMPROB_FIRSTFRAG_INCOMP_HDR, 0);
572 m = NULL;
573 goto done;
574 }
575 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
576 af6 = af6dwn) {
577 af6dwn = af6->ip6af_down;
578
579 if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen >
580 IPV6_MAXPACKET) {
581 struct mbuf *merr = IP6_REASS_MBUF(af6);
582 struct ip6_hdr *ip6err;
583 int erroff = af6->ip6af_offset;
584
585 /* dequeue the fragment. */
586 frag6_deq(af6);
587 ip6af_free(af6);
588
589 /* adjust pointer. */
590 ip6err = mtod(merr, struct ip6_hdr *);
591
592 /*
593 * Restore source and destination addresses
594 * in the erroneous IPv6 header.
595 */
596 ip6err->ip6_src = q6->ip6q_src;
597 ip6err->ip6_dst = q6->ip6q_dst;
598 ip6_output_setdstifscope(m, q6->ip6q_dst_ifscope, NULL);
599 ip6_output_setsrcifscope(m, q6->ip6q_src_ifscope, NULL);
600 frag6_save_context(merr,
601 erroff - sizeof(struct ip6_frag) +
602 offsetof(struct ip6_frag, ip6f_offlg));
603
604 MBUFQ_ENQUEUE(&diq6, merr);
605 }
606 }
607 }
608
609 ip6af = ip6af_alloc(M_DONTWAIT);
610 if (ip6af == NULL) {
611 goto dropfrag;
612 }
613
614 ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG;
615 ip6af->ip6af_off = fragoff;
616 ip6af->ip6af_frglen = frgpartlen;
617 ip6af->ip6af_offset = offset;
618 IP6_REASS_MBUF(ip6af) = m;
619
620 if (first_frag) {
621 af6 = (struct ip6asfrag *)q6;
622 goto insert;
623 }
624
625 /*
626 * Handle ECN by comparing this segment with the first one;
627 * if CE is set, do not lose CE.
628 * drop if CE and not-ECT are mixed for the same packet.
629 */
630 ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK;
631 ecn0 = q6->ip6q_ecn;
632 if (ecn == IPTOS_ECN_CE) {
633 if (ecn0 == IPTOS_ECN_NOTECT) {
634 ip6af_free(ip6af);
635 goto dropfrag;
636 }
637 if (ecn0 != IPTOS_ECN_CE) {
638 q6->ip6q_ecn = IPTOS_ECN_CE;
639 }
640 }
641 if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT) {
642 ip6af_free(ip6af);
643 goto dropfrag;
644 }
645
646 /*
647 * Find a segment which begins after this one does.
648 */
649 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
650 af6 = af6->ip6af_down) {
651 if (af6->ip6af_off > ip6af->ip6af_off) {
652 break;
653 }
654 }
655
656 /*
657 * As per RFC 8200 reassembly rules, we MUST drop the entire
658 * chain of fragments for a packet to be assembled, if we receive
659 * any overlapping fragments.
660 * https://tools.ietf.org/html/rfc8200#page-20
661 *
662 * To avoid more conditional code, just reuse frag6_freef and defer
663 * its call to post fragment insertion in the queue.
664 */
665 if (af6->ip6af_up != (struct ip6asfrag *)q6) {
666 if (af6->ip6af_up->ip6af_off == ip6af->ip6af_off) {
667 if (af6->ip6af_up->ip6af_frglen != ip6af->ip6af_frglen) {
668 drop_fragq = TRUE;
669 } else {
670 /*
671 * XXX Ideally we should be comparing the entire
672 * packet here but for now just use off and fraglen
673 * to ignore a duplicate fragment.
674 */
675 ip6af_free(ip6af);
676 goto dropfrag;
677 }
678 } else {
679 i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen
680 - ip6af->ip6af_off;
681 if (i > 0) {
682 drop_fragq = TRUE;
683 }
684 }
685 }
686
687 if (af6 != (struct ip6asfrag *)q6) {
688 /*
689 * Given that we break when af6->ip6af_off > ip6af->ip6af_off,
690 * we shouldn't need a check for duplicate fragment here.
691 * For now just assert.
692 */
693 VERIFY(af6->ip6af_off != ip6af->ip6af_off);
694 i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off;
695 if (i > 0) {
696 drop_fragq = TRUE;
697 }
698 }
699
700 /*
701 * If this fragment contains similar checksum offload info
702 * as that of the existing ones, accumulate checksum. Otherwise,
703 * invalidate checksum offload info for the entire datagram.
704 */
705 if (csum_flags != 0 && csum_flags == q6->ip6q_csum_flags) {
706 q6->ip6q_csum += csum;
707 } else if (q6->ip6q_csum_flags != 0) {
708 q6->ip6q_csum_flags = 0;
709 }
710
711 insert:
712 /*
713 * Stick new segment in its place;
714 * check for complete reassembly.
715 * Move to front of packet queue, as we are
716 * the most recently active fragmented packet.
717 */
718 frag6_enq(ip6af, af6->ip6af_up);
719 frag6_nfrags++;
720 q6->ip6q_nfrag++;
721
722 /*
723 * This holds true, when we receive overlapping fragments.
724 * We must silently drop all the fragments we have received
725 * so far.
726 * Also mark q6 as dirty, so as to not add any new fragments to it.
727 * Make sure even q6 marked dirty is kept till timer expires for
728 * reassembly and when that happens, silenty get rid of q6
729 */
730 if (drop_fragq) {
731 struct fq6_head dfq6 = {0};
732 MBUFQ_INIT(&dfq6); /* for deferred frees */
733 q6->ip6q_flags |= IP6QF_DIRTY;
734 /* Purge all the fragments but do not free q6 */
735 frag6_purgef(q6, &dfq6, NULL);
736 af6 = NULL;
737
738 /* free fragments that need to be freed */
739 if (!MBUFQ_EMPTY(&dfq6)) {
740 MBUFQ_DRAIN(&dfq6);
741 }
742 VERIFY(MBUFQ_EMPTY(&dfq6));
743 /*
744 * Just in case the above logic got anything added
745 * to diq6, drain it.
746 * Please note that these mbufs are not present in the
747 * fragment queue and are added to diq6 for sending
748 * ICMPv6 error.
749 * Given that the current fragment was an overlapping
750 * fragment and the RFC requires us to not send any
751 * ICMPv6 errors while purging the entire queue.
752 * Just empty it out.
753 */
754 if (!MBUFQ_EMPTY(&diq6)) {
755 MBUFQ_DRAIN(&diq6);
756 }
757 VERIFY(MBUFQ_EMPTY(&diq6));
758 /*
759 * MBUFQ_DRAIN would have drained all the mbufs
760 * in the fragment queue.
761 * This shouldn't be needed as we are returning IPPROTO_DONE
762 * from here but change the passed mbuf pointer to NULL.
763 */
764 *mp = NULL;
765 lck_mtx_unlock(&ip6qlock);
766 return IPPROTO_DONE;
767 }
768 next = 0;
769 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
770 af6 = af6->ip6af_down) {
771 if (af6->ip6af_off != next) {
772 lck_mtx_unlock(&ip6qlock);
773 locked = 0;
774 m = NULL;
775 goto done;
776 }
777 next += af6->ip6af_frglen;
778 }
779 if (af6->ip6af_up->ip6af_mff) {
780 lck_mtx_unlock(&ip6qlock);
781 locked = 0;
782 m = NULL;
783 goto done;
784 }
785
786 /*
787 * Reassembly is complete; concatenate fragments.
788 */
789 ip6af = q6->ip6q_down;
790 t = m = IP6_REASS_MBUF(ip6af);
791 af6 = ip6af->ip6af_down;
792 frag6_deq(ip6af);
793 while (af6 != (struct ip6asfrag *)q6) {
794 af6dwn = af6->ip6af_down;
795 frag6_deq(af6);
796 while (t->m_next) {
797 t = t->m_next;
798 }
799 t->m_next = IP6_REASS_MBUF(af6);
800 m_adj(t->m_next, af6->ip6af_offset);
801 ip6af_free(af6);
802 af6 = af6dwn;
803 }
804
805 /*
806 * Store partial hardware checksum info from the fragment queue;
807 * the receive start offset is set to 40 bytes (see code at the
808 * top of this routine.)
809 */
810 if (q6->ip6q_csum_flags != 0) {
811 csum = q6->ip6q_csum;
812
813 ADDCARRY(csum);
814
815 m->m_pkthdr.csum_rx_val = (u_int16_t)csum;
816 m->m_pkthdr.csum_rx_start = sizeof(struct ip6_hdr);
817 m->m_pkthdr.csum_flags = q6->ip6q_csum_flags;
818 } else if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) ||
819 (m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
820 /* loopback checksums are always OK */
821 m->m_pkthdr.csum_data = 0xffff;
822 m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
823 }
824
825 /* adjust offset to point where the original next header starts */
826 offset = ip6af->ip6af_offset - sizeof(struct ip6_frag);
827 ip6af_free(ip6af);
828 ip6 = mtod(m, struct ip6_hdr *);
829 ip6->ip6_plen = htons((uint16_t)(next + offset - sizeof(struct ip6_hdr)));
830 ip6->ip6_src = q6->ip6q_src;
831 ip6->ip6_dst = q6->ip6q_dst;
832 ip6_output_setdstifscope(m, q6->ip6q_dst_ifscope, NULL);
833 ip6_output_setsrcifscope(m, q6->ip6q_src_ifscope, NULL);
834 if (q6->ip6q_ecn == IPTOS_ECN_CE) {
835 ip6->ip6_flow |= htonl(IPTOS_ECN_CE << 20);
836 }
837
838 nxt = q6->ip6q_nxt;
839 #ifdef notyet
840 *q6->ip6q_nxtp = (u_char)(nxt & 0xff);
841 #endif
842
843 /* Delete frag6 header */
844 if (m->m_len >= offset + sizeof(struct ip6_frag)) {
845 /* This is the only possible case with !PULLDOWN_TEST */
846 ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag),
847 offset);
848 m->m_data += sizeof(struct ip6_frag);
849 m->m_len -= sizeof(struct ip6_frag);
850 } else {
851 /* this comes with no copy if the boundary is on cluster */
852 if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) {
853 frag6_remque(q6);
854 frag6_nfragpackets--;
855 frag6_nfrags -= q6->ip6q_nfrag;
856 ip6q_free(q6);
857 goto dropfrag;
858 }
859 m_adj(t, sizeof(struct ip6_frag));
860 m_cat(m, t);
861 }
862
863 /*
864 * Store NXT to the original.
865 */
866 {
867 char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */
868 *prvnxtp = nxt;
869 }
870
871 frag6_remque(q6);
872 frag6_nfragpackets--;
873 frag6_nfrags -= q6->ip6q_nfrag;
874 ip6q_free(q6);
875
876 if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */
877 m_fixhdr(m);
878 /*
879 * Mark packet as reassembled
880 * In ICMPv6 processing, we drop certain
881 * NDP messages that are not expected to
882 * have fragment header based on recommendations
883 * against security vulnerability as described in
884 * RFC 6980.
885 */
886 m->m_pkthdr.pkt_flags |= PKTF_REASSEMBLED;
887 }
888 ip6stat.ip6s_reassembled++;
889
890 /*
891 * Tell launch routine the next header
892 */
893 *mp = m;
894 *offp = offset;
895
896 /* arm the purge timer if not already and if there's work to do */
897 frag6_sched_timeout();
898 lck_mtx_unlock(&ip6qlock);
899 in6_ifstat_inc(dstifp, ifs6_reass_ok);
900 frag6_icmp6_paramprob_error(&diq6);
901 VERIFY(MBUFQ_EMPTY(&diq6));
902 return nxt;
903
904 done:
905 VERIFY(m == NULL);
906 *mp = m;
907 if (!locked) {
908 if (frag6_nfragpackets == 0) {
909 frag6_icmp6_paramprob_error(&diq6);
910 VERIFY(MBUFQ_EMPTY(&diq6));
911 return IPPROTO_DONE;
912 }
913 lck_mtx_lock(&ip6qlock);
914 }
915 /* arm the purge timer if not already and if there's work to do */
916 frag6_sched_timeout();
917 lck_mtx_unlock(&ip6qlock);
918 frag6_icmp6_paramprob_error(&diq6);
919 VERIFY(MBUFQ_EMPTY(&diq6));
920 return IPPROTO_DONE;
921
922 dropfrag:
923 ip6stat.ip6s_fragdropped++;
924 /* arm the purge timer if not already and if there's work to do */
925 frag6_sched_timeout();
926 lck_mtx_unlock(&ip6qlock);
927 in6_ifstat_inc(dstifp, ifs6_reass_fail);
928 m_freem(m);
929 *mp = NULL;
930 frag6_icmp6_paramprob_error(&diq6);
931 VERIFY(MBUFQ_EMPTY(&diq6));
932 return IPPROTO_DONE;
933 }
934
935 /*
936 * This routine removes the enqueued frames from the passed fragment
937 * header and enqueues those to dfq6 which is an out-arg for the dequeued
938 * fragments.
939 * If the caller also provides diq6, this routine also enqueues the 0 offset
940 * fragment to that list as it potentially gets used by the caller
941 * to prepare the relevant ICMPv6 error message (time exceeded or
942 * param problem).
943 * It leaves the fragment header object (q6) intact.
944 */
945 static void
frag6_purgef(struct ip6q * q6,struct fq6_head * dfq6,struct fq6_head * diq6)946 frag6_purgef(struct ip6q *q6, struct fq6_head *dfq6, struct fq6_head *diq6)
947 {
948 struct ip6asfrag *af6 = NULL;
949 struct ip6asfrag *down6 = NULL;
950
951 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
952
953 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
954 af6 = down6) {
955 struct mbuf *m = IP6_REASS_MBUF(af6);
956
957 down6 = af6->ip6af_down;
958 frag6_deq(af6);
959
960 /*
961 * If caller wants to generate ICMP time-exceeded,
962 * as indicated by the argument diq6, return it for
963 * the first fragment and add others to the fragment
964 * free queue.
965 */
966 if (af6->ip6af_off == 0 && diq6 != NULL) {
967 struct ip6_hdr *ip6;
968
969 /* adjust pointer */
970 ip6 = mtod(m, struct ip6_hdr *);
971
972 /* restore source and destination addresses */
973 ip6->ip6_src = q6->ip6q_src;
974 ip6->ip6_dst = q6->ip6q_dst;
975 ip6_output_setdstifscope(m, q6->ip6q_dst_ifscope, NULL);
976 ip6_output_setsrcifscope(m, q6->ip6q_src_ifscope, NULL);
977 MBUFQ_ENQUEUE(diq6, m);
978 } else {
979 MBUFQ_ENQUEUE(dfq6, m);
980 }
981 ip6af_free(af6);
982 }
983 }
984
985 /*
986 * This routine removes the enqueued frames from the passed fragment
987 * header and enqueues those to dfq6 which is an out-arg for the dequeued
988 * fragments.
989 * If the caller also provides diq6, this routine also enqueues the 0 offset
990 * fragment to that list as it potentially gets used by the caller
991 * to prepare the relevant ICMPv6 error message (time exceeded or
992 * param problem).
993 * It also remove the fragment header object from the queue and frees it.
994 */
995 static void
frag6_freef(struct ip6q * q6,struct fq6_head * dfq6,struct fq6_head * diq6)996 frag6_freef(struct ip6q *q6, struct fq6_head *dfq6, struct fq6_head *diq6)
997 {
998 frag6_purgef(q6, dfq6, diq6);
999 frag6_remque(q6);
1000 frag6_nfragpackets--;
1001 frag6_nfrags -= q6->ip6q_nfrag;
1002 ip6q_free(q6);
1003 }
1004
1005 /*
1006 * Put an ip fragment on a reassembly chain.
1007 * Like insque, but pointers in middle of structure.
1008 */
1009 void
frag6_enq(struct ip6asfrag * af6,struct ip6asfrag * up6)1010 frag6_enq(struct ip6asfrag *af6, struct ip6asfrag *up6)
1011 {
1012 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
1013
1014 af6->ip6af_up = up6;
1015 af6->ip6af_down = up6->ip6af_down;
1016 up6->ip6af_down->ip6af_up = af6;
1017 up6->ip6af_down = af6;
1018 }
1019
1020 /*
1021 * To frag6_enq as remque is to insque.
1022 */
1023 void
frag6_deq(struct ip6asfrag * af6)1024 frag6_deq(struct ip6asfrag *af6)
1025 {
1026 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
1027
1028 af6->ip6af_up->ip6af_down = af6->ip6af_down;
1029 af6->ip6af_down->ip6af_up = af6->ip6af_up;
1030 }
1031
1032 void
frag6_insque(struct ip6q * new,struct ip6q * old)1033 frag6_insque(struct ip6q *new, struct ip6q *old)
1034 {
1035 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
1036
1037 new->ip6q_prev = old;
1038 new->ip6q_next = old->ip6q_next;
1039 old->ip6q_next->ip6q_prev = new;
1040 old->ip6q_next = new;
1041 }
1042
1043 void
frag6_remque(struct ip6q * p6)1044 frag6_remque(struct ip6q *p6)
1045 {
1046 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
1047
1048 p6->ip6q_prev->ip6q_next = p6->ip6q_next;
1049 p6->ip6q_next->ip6q_prev = p6->ip6q_prev;
1050 }
1051
1052 /*
1053 * IPv6 reassembling timer processing;
1054 * if a timer expires on a reassembly
1055 * queue, discard it.
1056 */
1057 static void
frag6_timeout(void * arg)1058 frag6_timeout(void *arg)
1059 {
1060 #pragma unused(arg)
1061 struct fq6_head dfq6, diq6;
1062 struct fq6_head *diq6_tmp = NULL;
1063 struct ip6q *q6;
1064
1065 MBUFQ_INIT(&dfq6); /* for deferred frees */
1066 MBUFQ_INIT(&diq6); /* for deferred ICMP time exceeded errors */
1067
1068 /*
1069 * Update coarse-grained networking timestamp (in sec.); the idea
1070 * is to piggy-back on the timeout callout to update the counter
1071 * returnable via net_uptime().
1072 */
1073 net_update_uptime();
1074
1075 lck_mtx_lock(&ip6qlock);
1076 q6 = ip6q.ip6q_next;
1077 if (q6) {
1078 while (q6 != &ip6q) {
1079 --q6->ip6q_ttl;
1080 q6 = q6->ip6q_next;
1081 if (q6->ip6q_prev->ip6q_ttl == 0) {
1082 ip6stat.ip6s_fragtimeout++;
1083 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
1084 /*
1085 * Avoid sending ICMPv6 Time Exceeded for fragment headers
1086 * that are marked dirty.
1087 */
1088 diq6_tmp = (q6->ip6q_prev->ip6q_flags & IP6QF_DIRTY) ?
1089 NULL : &diq6;
1090 frag6_freef(q6->ip6q_prev, &dfq6, diq6_tmp);
1091 }
1092 }
1093 }
1094 /*
1095 * If we are over the maximum number of fragments
1096 * (due to the limit being lowered), drain off
1097 * enough to get down to the new limit.
1098 */
1099 if (ip6_maxfragpackets >= 0) {
1100 while (frag6_nfragpackets > (unsigned)ip6_maxfragpackets &&
1101 ip6q.ip6q_prev) {
1102 ip6stat.ip6s_fragoverflow++;
1103 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
1104 /*
1105 * Avoid sending ICMPv6 Time Exceeded for fragment headers
1106 * that are marked dirty.
1107 */
1108 diq6_tmp = (ip6q.ip6q_prev->ip6q_flags & IP6QF_DIRTY) ?
1109 NULL : &diq6;
1110 frag6_freef(ip6q.ip6q_prev, &dfq6, diq6_tmp);
1111 }
1112 }
1113 /* re-arm the purge timer if there's work to do */
1114 frag6_timeout_run = 0;
1115 frag6_sched_timeout();
1116 lck_mtx_unlock(&ip6qlock);
1117
1118 /* free fragments that need to be freed */
1119 if (!MBUFQ_EMPTY(&dfq6)) {
1120 MBUFQ_DRAIN(&dfq6);
1121 }
1122
1123 frag6_icmp6_timeex_error(&diq6);
1124
1125 VERIFY(MBUFQ_EMPTY(&dfq6));
1126 VERIFY(MBUFQ_EMPTY(&diq6));
1127 }
1128
1129 static void
frag6_sched_timeout(void)1130 frag6_sched_timeout(void)
1131 {
1132 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
1133
1134 if (!frag6_timeout_run && frag6_nfragpackets > 0) {
1135 frag6_timeout_run = 1;
1136 timeout(frag6_timeout, NULL, hz);
1137 }
1138 }
1139
1140 /*
1141 * Drain off all datagram fragments.
1142 */
1143 void
frag6_drain(void)1144 frag6_drain(void)
1145 {
1146 struct fq6_head dfq6, diq6;
1147 struct fq6_head *diq6_tmp = NULL;
1148
1149 MBUFQ_INIT(&dfq6); /* for deferred frees */
1150 MBUFQ_INIT(&diq6); /* for deferred ICMP time exceeded errors */
1151
1152 lck_mtx_lock(&ip6qlock);
1153 while (ip6q.ip6q_next != &ip6q) {
1154 ip6stat.ip6s_fragdropped++;
1155 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
1156 /*
1157 * Avoid sending ICMPv6 Time Exceeded for fragment headers
1158 * that are marked dirty.
1159 */
1160 diq6_tmp = (ip6q.ip6q_next->ip6q_flags & IP6QF_DIRTY) ?
1161 NULL : &diq6;
1162 frag6_freef(ip6q.ip6q_next, &dfq6, diq6_tmp);
1163 }
1164 lck_mtx_unlock(&ip6qlock);
1165
1166 /* free fragments that need to be freed */
1167 if (!MBUFQ_EMPTY(&dfq6)) {
1168 MBUFQ_DRAIN(&dfq6);
1169 }
1170
1171 frag6_icmp6_timeex_error(&diq6);
1172
1173 VERIFY(MBUFQ_EMPTY(&dfq6));
1174 VERIFY(MBUFQ_EMPTY(&diq6));
1175 }
1176
1177 static struct ip6q *
ip6q_alloc(int how)1178 ip6q_alloc(int how)
1179 {
1180 struct mbuf *t;
1181 struct ip6q *q6;
1182
1183 /*
1184 * See comments in ip6q_updateparams(). Keep the count separate
1185 * from frag6_nfragpackets since the latter represents the elements
1186 * already in the reassembly queues.
1187 */
1188 if (ip6q_limit > 0 && ip6q_count > ip6q_limit) {
1189 return NULL;
1190 }
1191
1192 t = m_get(how, MT_FTABLE);
1193 if (t != NULL) {
1194 atomic_add_32(&ip6q_count, 1);
1195 q6 = mtod(t, struct ip6q *);
1196 bzero(q6, sizeof(*q6));
1197 } else {
1198 q6 = NULL;
1199 }
1200 return q6;
1201 }
1202
1203 static void
ip6q_free(struct ip6q * q6)1204 ip6q_free(struct ip6q *q6)
1205 {
1206 (void) m_free(dtom(q6));
1207 atomic_add_32(&ip6q_count, -1);
1208 }
1209
1210 static struct ip6asfrag *
ip6af_alloc(int how)1211 ip6af_alloc(int how)
1212 {
1213 struct mbuf *t;
1214 struct ip6asfrag *af6;
1215
1216 /*
1217 * See comments in ip6q_updateparams(). Keep the count separate
1218 * from frag6_nfrags since the latter represents the elements
1219 * already in the reassembly queues.
1220 */
1221 if (ip6af_limit > 0 && ip6af_count > ip6af_limit) {
1222 return NULL;
1223 }
1224
1225 t = m_get(how, MT_FTABLE);
1226 if (t != NULL) {
1227 atomic_add_32(&ip6af_count, 1);
1228 af6 = mtod(t, struct ip6asfrag *);
1229 bzero(af6, sizeof(*af6));
1230 } else {
1231 af6 = NULL;
1232 }
1233 return af6;
1234 }
1235
1236 static void
ip6af_free(struct ip6asfrag * af6)1237 ip6af_free(struct ip6asfrag *af6)
1238 {
1239 (void) m_free(dtom(af6));
1240 atomic_add_32(&ip6af_count, -1);
1241 }
1242
1243 static void
ip6q_updateparams(void)1244 ip6q_updateparams(void)
1245 {
1246 LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
1247 /*
1248 * -1 for unlimited allocation.
1249 */
1250 if (ip6_maxfragpackets < 0) {
1251 ip6q_limit = 0;
1252 }
1253 if (ip6_maxfrags < 0) {
1254 ip6af_limit = 0;
1255 }
1256 /*
1257 * Positive number for specific bound.
1258 */
1259 if (ip6_maxfragpackets > 0) {
1260 ip6q_limit = ip6_maxfragpackets;
1261 }
1262 if (ip6_maxfrags > 0) {
1263 ip6af_limit = ip6_maxfrags;
1264 }
1265 /*
1266 * Zero specifies no further fragment queue allocation -- set the
1267 * bound very low, but rely on implementation elsewhere to actually
1268 * prevent allocation and reclaim current queues.
1269 */
1270 if (ip6_maxfragpackets == 0) {
1271 ip6q_limit = 1;
1272 }
1273 if (ip6_maxfrags == 0) {
1274 ip6af_limit = 1;
1275 }
1276 /*
1277 * Arm the purge timer if not already and if there's work to do
1278 */
1279 frag6_sched_timeout();
1280 }
1281
1282 static int
1283 sysctl_maxfragpackets SYSCTL_HANDLER_ARGS
1284 {
1285 #pragma unused(arg1, arg2)
1286 int error, i;
1287
1288 lck_mtx_lock(&ip6qlock);
1289 i = ip6_maxfragpackets;
1290 error = sysctl_handle_int(oidp, &i, 0, req);
1291 if (error || req->newptr == USER_ADDR_NULL) {
1292 goto done;
1293 }
1294 /* impose bounds */
1295 if (i < -1 || i > (nmbclusters / 4)) {
1296 error = EINVAL;
1297 goto done;
1298 }
1299 ip6_maxfragpackets = i;
1300 ip6q_updateparams();
1301 done:
1302 lck_mtx_unlock(&ip6qlock);
1303 return error;
1304 }
1305
1306 static int
1307 sysctl_maxfrags SYSCTL_HANDLER_ARGS
1308 {
1309 #pragma unused(arg1, arg2)
1310 int error, i;
1311
1312 lck_mtx_lock(&ip6qlock);
1313 i = ip6_maxfrags;
1314 error = sysctl_handle_int(oidp, &i, 0, req);
1315 if (error || req->newptr == USER_ADDR_NULL) {
1316 goto done;
1317 }
1318 /* impose bounds */
1319 if (i < -1 || i > (nmbclusters / 4)) {
1320 error = EINVAL;
1321 goto done;
1322 }
1323 ip6_maxfrags = i;
1324 ip6q_updateparams(); /* see if we need to arm timer */
1325 done:
1326 lck_mtx_unlock(&ip6qlock);
1327 return error;
1328 }
1329