2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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 $ */
33 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
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.
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
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/malloc.h>
65 #include <sys/domain.h>
66 #include <sys/protosw.h>
67 #include <sys/socket.h>
68 #include <sys/errno.h>
70 #include <sys/kernel.h>
71 #include <sys/syslog.h>
72 #include <kern/queue.h>
73 #include <kern/locks.h>
76 #include <net/route.h>
78 #include <netinet/in.h>
79 #include <netinet/in_var.h>
80 #include <netinet/ip6.h>
81 #include <netinet6/ip6_var.h>
82 #include <netinet/icmp6.h>
84 #include <net/net_osdep.h>
87 * Define it to get a correct behavior on per-interface statistics.
88 * You will need to perform an extra routing table lookup, per fragment,
89 * to do it. This may, or may not be, a performance hit.
91 #define IN6_IFSTAT_STRICT
93 static void frag6_enq(struct ip6asfrag
*, struct ip6asfrag
*);
94 static void frag6_deq(struct ip6asfrag
*);
95 static void frag6_insque(struct ip6q
*, struct ip6q
*);
96 static void frag6_remque(struct ip6q
*);
97 static void frag6_freef(struct ip6q
*);
99 /* XXX we eventually need splreass6, or some real semaphore */
100 int frag6_doing_reass
;
101 u_int frag6_nfragpackets
;
102 static u_int frag6_nfrags
;
103 struct ip6q ip6q
; /* ip6 reassemble queue */
106 MALLOC_DEFINE(M_FTABLE
, "fragment", "fragment reassembly header");
109 extern lck_mtx_t
*inet6_domain_mutex
;
111 * Initialise reassembly queue and fragment identifier.
118 ip6_maxfragpackets
= nmbclusters
/ 32;
119 ip6_maxfrags
= nmbclusters
/ 4;
122 * in many cases, random() here does NOT return random number
123 * as initialization during bootstrap time occur in fixed order.
126 ip6_id
= random() ^ tv
.tv_usec
;
127 ip6q
.ip6q_next
= ip6q
.ip6q_prev
= &ip6q
;
131 * In RFC2460, fragment and reassembly rule do not agree with each other,
132 * in terms of next header field handling in fragment header.
133 * While the sender will use the same value for all of the fragmented packets,
134 * receiver is suggested not to check the consistency.
136 * fragment rule (p20):
137 * (2) A Fragment header containing:
138 * The Next Header value that identifies the first header of
139 * the Fragmentable Part of the original packet.
140 * -> next header field is same for all fragments
142 * reassembly rule (p21):
143 * The Next Header field of the last header of the Unfragmentable
144 * Part is obtained from the Next Header field of the first
145 * fragment's Fragment header.
146 * -> should grab it from the first fragment only
148 * The following note also contradicts with fragment rule - noone is going to
149 * send different fragment with different next header field.
151 * additional note (p22):
152 * The Next Header values in the Fragment headers of different
153 * fragments of the same original packet may differ. Only the value
154 * from the Offset zero fragment packet is used for reassembly.
155 * -> should grab it from the first fragment only
157 * There is no explicit reason given in the RFC. Historical reason maybe?
161 * NOTE: this function is called with the inet6_domain_mutex held from ip6_input.
162 * inet6_domain_mutex is protecting he frag6 queue manipulation.
165 frag6_input(mp
, offp
)
169 struct mbuf
*m
= *mp
, *t
;
171 struct ip6_frag
*ip6f
;
173 struct ip6asfrag
*af6
, *ip6af
, *af6dwn
;
174 int offset
= *offp
, nxt
, i
, next
;
176 int fragoff
, frgpartlen
; /* must be larger than u_int16_t */
177 struct ifnet
*dstifp
;
178 struct ifaddr
*ifa
= NULL
;
179 #ifdef IN6_IFSTAT_STRICT
181 struct sockaddr_in6
*dst
;
184 ip6
= mtod(m
, struct ip6_hdr
*);
185 #ifndef PULLDOWN_TEST
186 IP6_EXTHDR_CHECK(m
, offset
, sizeof(struct ip6_frag
), return IPPROTO_DONE
);
187 ip6f
= (struct ip6_frag
*)((caddr_t
)ip6
+ offset
);
189 IP6_EXTHDR_GET(ip6f
, struct ip6_frag
*, m
, offset
, sizeof(*ip6f
));
195 #ifdef IN6_IFSTAT_STRICT
196 /* find the destination interface of the packet. */
197 bzero(&ro
, sizeof (ro
));
198 dst
= (struct sockaddr_in6
*)&ro
.ro_dst
;
199 dst
->sin6_family
= AF_INET6
;
200 dst
->sin6_len
= sizeof (struct sockaddr_in6
);
201 dst
->sin6_addr
= ip6
->ip6_dst
;
203 rtalloc((struct route
*)&ro
);
204 if (ro
.ro_rt
!= NULL
) {
206 if ((ifa
= ro
.ro_rt
->rt_ifa
) != NULL
) {
208 dstifp
= ((struct in6_ifaddr
*)ro
.ro_rt
->rt_ifa
)->ia_ifp
;
215 /* we are violating the spec, this is not the destination interface */
216 if ((m
->m_flags
& M_PKTHDR
) != 0)
217 dstifp
= m
->m_pkthdr
.rcvif
;
220 /* jumbo payload can't contain a fragment header */
221 if (ip6
->ip6_plen
== 0) {
222 icmp6_error(m
, ICMP6_PARAM_PROB
, ICMP6_PARAMPROB_HEADER
, offset
);
223 in6_ifstat_inc(dstifp
, ifs6_reass_fail
);
230 * check whether fragment packet's fragment length is
231 * multiple of 8 octets.
232 * sizeof(struct ip6_frag) == 8
233 * sizeof(struct ip6_hdr) = 40
235 if ((ip6f
->ip6f_offlg
& IP6F_MORE_FRAG
) &&
236 (((ntohs(ip6
->ip6_plen
) - offset
) & 0x7) != 0)) {
237 icmp6_error(m
, ICMP6_PARAM_PROB
,
238 ICMP6_PARAMPROB_HEADER
,
239 offsetof(struct ip6_hdr
, ip6_plen
));
240 in6_ifstat_inc(dstifp
, ifs6_reass_fail
);
246 ip6stat
.ip6s_fragments
++;
247 in6_ifstat_inc(dstifp
, ifs6_reass_reqd
);
249 /* offset now points to data portion */
250 offset
+= sizeof(struct ip6_frag
);
252 frag6_doing_reass
= 1;
255 * Enforce upper bound on number of fragments.
256 * If maxfrag is 0, never accept fragments.
257 * If maxfrag is -1, accept all fragments without limitation.
259 if (ip6_maxfrags
< 0)
261 else if (frag6_nfrags
>= (u_int
)ip6_maxfrags
)
264 for (q6
= ip6q
.ip6q_next
; q6
!= &ip6q
; q6
= q6
->ip6q_next
)
265 if (ip6f
->ip6f_ident
== q6
->ip6q_ident
&&
266 IN6_ARE_ADDR_EQUAL(&ip6
->ip6_src
, &q6
->ip6q_src
) &&
267 IN6_ARE_ADDR_EQUAL(&ip6
->ip6_dst
, &q6
->ip6q_dst
))
272 * the first fragment to arrive, create a reassembly queue.
277 * Enforce upper bound on number of fragmented packets
278 * for which we attempt reassembly;
279 * If maxfrag is 0, never accept fragments.
280 * If maxfrag is -1, accept all fragments without limitation.
282 if (ip6_maxfragpackets
< 0)
284 else if (frag6_nfragpackets
>= (u_int
)ip6_maxfragpackets
)
286 frag6_nfragpackets
++;
287 q6
= (struct ip6q
*)_MALLOC(sizeof(struct ip6q
), M_FTABLE
,
291 bzero(q6
, sizeof(*q6
));
293 frag6_insque(q6
, &ip6q
);
295 /* ip6q_nxt will be filled afterwards, from 1st fragment */
296 q6
->ip6q_down
= q6
->ip6q_up
= (struct ip6asfrag
*)q6
;
298 q6
->ip6q_nxtp
= (u_char
*)nxtp
;
300 q6
->ip6q_ident
= ip6f
->ip6f_ident
;
301 q6
->ip6q_arrive
= 0; /* Is it used anywhere? */
302 q6
->ip6q_ttl
= IPV6_FRAGTTL
;
303 q6
->ip6q_src
= ip6
->ip6_src
;
304 q6
->ip6q_dst
= ip6
->ip6_dst
;
305 q6
->ip6q_unfrglen
= -1; /* The 1st fragment has not arrived. */
311 * If it's the 1st fragment, record the length of the
312 * unfragmentable part and the next header of the fragment header.
314 fragoff
= ntohs(ip6f
->ip6f_offlg
& IP6F_OFF_MASK
);
316 q6
->ip6q_unfrglen
= offset
- sizeof(struct ip6_hdr
)
317 - sizeof(struct ip6_frag
);
318 q6
->ip6q_nxt
= ip6f
->ip6f_nxt
;
322 * Check that the reassembled packet would not exceed 65535 bytes
324 * If it would exceed, discard the fragment and return an ICMP error.
326 frgpartlen
= sizeof(struct ip6_hdr
) + ntohs(ip6
->ip6_plen
) - offset
;
327 if (q6
->ip6q_unfrglen
>= 0) {
328 /* The 1st fragment has already arrived. */
329 if (q6
->ip6q_unfrglen
+ fragoff
+ frgpartlen
> IPV6_MAXPACKET
) {
330 icmp6_error(m
, ICMP6_PARAM_PROB
, ICMP6_PARAMPROB_HEADER
,
331 offset
- sizeof(struct ip6_frag
) +
332 offsetof(struct ip6_frag
, ip6f_offlg
));
333 frag6_doing_reass
= 0;
336 return(IPPROTO_DONE
);
339 else if (fragoff
+ frgpartlen
> IPV6_MAXPACKET
) {
340 icmp6_error(m
, ICMP6_PARAM_PROB
, ICMP6_PARAMPROB_HEADER
,
341 offset
- sizeof(struct ip6_frag
) +
342 offsetof(struct ip6_frag
, ip6f_offlg
));
343 frag6_doing_reass
= 0;
346 return(IPPROTO_DONE
);
349 * If it's the first fragment, do the above check for each
350 * fragment already stored in the reassembly queue.
353 for (af6
= q6
->ip6q_down
; af6
!= (struct ip6asfrag
*)q6
;
355 af6dwn
= af6
->ip6af_down
;
357 if (q6
->ip6q_unfrglen
+ af6
->ip6af_off
+ af6
->ip6af_frglen
>
359 struct mbuf
*merr
= IP6_REASS_MBUF(af6
);
360 struct ip6_hdr
*ip6err
;
361 int erroff
= af6
->ip6af_offset
;
363 /* dequeue the fragment. */
367 /* adjust pointer. */
368 ip6err
= mtod(merr
, struct ip6_hdr
*);
371 * Restore source and destination addresses
372 * in the erroneous IPv6 header.
374 ip6err
->ip6_src
= q6
->ip6q_src
;
375 ip6err
->ip6_dst
= q6
->ip6q_dst
;
377 icmp6_error(merr
, ICMP6_PARAM_PROB
,
378 ICMP6_PARAMPROB_HEADER
,
379 erroff
- sizeof(struct ip6_frag
) +
380 offsetof(struct ip6_frag
, ip6f_offlg
));
385 ip6af
= (struct ip6asfrag
*)_MALLOC(sizeof(struct ip6asfrag
), M_FTABLE
,
389 bzero(ip6af
, sizeof(*ip6af
));
390 ip6af
->ip6af_head
= ip6
->ip6_flow
;
391 ip6af
->ip6af_len
= ip6
->ip6_plen
;
392 ip6af
->ip6af_nxt
= ip6
->ip6_nxt
;
393 ip6af
->ip6af_hlim
= ip6
->ip6_hlim
;
394 ip6af
->ip6af_mff
= ip6f
->ip6f_offlg
& IP6F_MORE_FRAG
;
395 ip6af
->ip6af_off
= fragoff
;
396 ip6af
->ip6af_frglen
= frgpartlen
;
397 ip6af
->ip6af_offset
= offset
;
398 IP6_REASS_MBUF(ip6af
) = m
;
401 af6
= (struct ip6asfrag
*)q6
;
406 * Find a segment which begins after this one does.
408 for (af6
= q6
->ip6q_down
; af6
!= (struct ip6asfrag
*)q6
;
409 af6
= af6
->ip6af_down
)
410 if (af6
->ip6af_off
> ip6af
->ip6af_off
)
415 * If there is a preceding segment, it may provide some of
416 * our data already. If so, drop the data from the incoming
417 * segment. If it provides all of our data, drop us.
419 if (af6
->ip6af_up
!= (struct ip6asfrag
*)q6
) {
420 i
= af6
->ip6af_up
->ip6af_off
+ af6
->ip6af_up
->ip6af_frglen
423 if (i
>= ip6af
->ip6af_frglen
)
425 m_adj(IP6_REASS_MBUF(ip6af
), i
);
426 ip6af
->ip6af_off
+= i
;
427 ip6af
->ip6af_frglen
-= i
;
432 * While we overlap succeeding segments trim them or,
433 * if they are completely covered, dequeue them.
435 while (af6
!= (struct ip6asfrag
*)q6
&&
436 ip6af
->ip6af_off
+ ip6af
->ip6af_frglen
> af6
->ip6af_off
) {
437 i
= (ip6af
->ip6af_off
+ ip6af
->ip6af_frglen
) - af6
->ip6af_off
;
438 if (i
< af6
->ip6af_frglen
) {
439 af6
->ip6af_frglen
-= i
;
441 m_adj(IP6_REASS_MBUF(af6
), i
);
444 af6
= af6
->ip6af_down
;
445 m_freem(IP6_REASS_MBUF(af6
->ip6af_up
));
446 frag6_deq(af6
->ip6af_up
);
450 * If the incoming framgent overlaps some existing fragments in
451 * the reassembly queue, drop it, since it is dangerous to override
452 * existing fragments from a security point of view.
454 if (af6
->ip6af_up
!= (struct ip6asfrag
*)q6
) {
455 i
= af6
->ip6af_up
->ip6af_off
+ af6
->ip6af_up
->ip6af_frglen
458 #if 0 /* suppress the noisy log */
459 log(LOG_ERR
, "%d bytes of a fragment from %s "
460 "overlaps the previous fragment\n",
461 i
, ip6_sprintf(&q6
->ip6q_src
));
463 FREE(ip6af
, M_FTABLE
);
467 if (af6
!= (struct ip6asfrag
*)q6
) {
468 i
= (ip6af
->ip6af_off
+ ip6af
->ip6af_frglen
) - af6
->ip6af_off
;
470 #if 0 /* suppress the noisy log */
471 log(LOG_ERR
, "%d bytes of a fragment from %s "
472 "overlaps the succeeding fragment",
473 i
, ip6_sprintf(&q6
->ip6q_src
));
475 FREE(ip6af
, M_FTABLE
);
484 * Stick new segment in its place;
485 * check for complete reassembly.
486 * Move to front of packet queue, as we are
487 * the most recently active fragmented packet.
489 frag6_enq(ip6af
, af6
->ip6af_up
);
493 if (q6
!= ip6q
.ip6q_next
) {
495 frag6_insque(q6
, &ip6q
);
499 for (af6
= q6
->ip6q_down
; af6
!= (struct ip6asfrag
*)q6
;
500 af6
= af6
->ip6af_down
) {
501 if (af6
->ip6af_off
!= next
) {
502 frag6_doing_reass
= 0;
507 next
+= af6
->ip6af_frglen
;
509 if (af6
->ip6af_up
->ip6af_mff
) {
510 frag6_doing_reass
= 0;
517 * Reassembly is complete; concatenate fragments.
519 ip6af
= q6
->ip6q_down
;
520 t
= m
= IP6_REASS_MBUF(ip6af
);
521 af6
= ip6af
->ip6af_down
;
523 while (af6
!= (struct ip6asfrag
*)q6
) {
524 af6dwn
= af6
->ip6af_down
;
528 t
->m_next
= IP6_REASS_MBUF(af6
);
529 m_adj(t
->m_next
, af6
->ip6af_offset
);
534 /* adjust offset to point where the original next header starts */
535 offset
= ip6af
->ip6af_offset
- sizeof(struct ip6_frag
);
536 FREE(ip6af
, M_FTABLE
);
537 ip6
= mtod(m
, struct ip6_hdr
*);
538 ip6
->ip6_plen
= htons((u_short
)next
+ offset
- sizeof(struct ip6_hdr
));
539 ip6
->ip6_src
= q6
->ip6q_src
;
540 ip6
->ip6_dst
= q6
->ip6q_dst
;
543 *q6
->ip6q_nxtp
= (u_char
)(nxt
& 0xff);
547 * Delete frag6 header with as a few cost as possible.
549 if (offset
< m
->m_len
) {
550 ovbcopy((caddr_t
)ip6
, (caddr_t
)ip6
+ sizeof(struct ip6_frag
),
552 m
->m_data
+= sizeof(struct ip6_frag
);
553 m
->m_len
-= sizeof(struct ip6_frag
);
555 /* this comes with no copy if the boundary is on cluster */
556 if ((t
= m_split(m
, offset
, M_DONTWAIT
)) == NULL
) {
558 frag6_nfrags
-= q6
->ip6q_nfrag
;
560 frag6_nfragpackets
--;
563 m_adj(t
, sizeof(struct ip6_frag
));
568 * Store NXT to the original.
571 char *prvnxtp
= ip6_get_prevhdr(m
, offset
); /* XXX */
576 frag6_nfrags
-= q6
->ip6q_nfrag
;
578 frag6_nfragpackets
--;
580 if (m
->m_flags
& M_PKTHDR
) { /* Isn't it always true? */
582 for (t
= m
; t
; t
= t
->m_next
)
584 m
->m_pkthdr
.len
= plen
;
587 ip6stat
.ip6s_reassembled
++;
588 in6_ifstat_inc(dstifp
, ifs6_reass_ok
);
591 * Tell launch routine the next header
597 frag6_doing_reass
= 0;
603 in6_ifstat_inc(dstifp
, ifs6_reass_fail
);
604 ip6stat
.ip6s_fragdropped
++;
606 frag6_doing_reass
= 0;
613 * Free a fragment reassembly header and all
614 * associated datagrams.
620 struct ip6asfrag
*af6
, *down6
;
622 for (af6
= q6
->ip6q_down
; af6
!= (struct ip6asfrag
*)q6
;
624 struct mbuf
*m
= IP6_REASS_MBUF(af6
);
626 down6
= af6
->ip6af_down
;
630 * Return ICMP time exceeded error for the 1st fragment.
631 * Just free other fragments.
633 if (af6
->ip6af_off
== 0) {
637 ip6
= mtod(m
, struct ip6_hdr
*);
639 /* restoure source and destination addresses */
640 ip6
->ip6_src
= q6
->ip6q_src
;
641 ip6
->ip6_dst
= q6
->ip6q_dst
;
642 icmp6_error(m
, ICMP6_TIME_EXCEEDED
,
643 ICMP6_TIME_EXCEED_REASSEMBLY
, 0);
650 frag6_nfrags
-= q6
->ip6q_nfrag
;
652 frag6_nfragpackets
--;
656 * Put an ip fragment on a reassembly chain.
657 * Like insque, but pointers in middle of structure.
661 struct ip6asfrag
*af6
, *up6
;
664 af6
->ip6af_down
= up6
->ip6af_down
;
665 up6
->ip6af_down
->ip6af_up
= af6
;
666 up6
->ip6af_down
= af6
;
670 * To frag6_enq as remque is to insque.
674 struct ip6asfrag
*af6
;
676 af6
->ip6af_up
->ip6af_down
= af6
->ip6af_down
;
677 af6
->ip6af_down
->ip6af_up
= af6
->ip6af_up
;
681 frag6_insque(new, old
)
682 struct ip6q
*new, *old
;
684 new->ip6q_prev
= old
;
685 new->ip6q_next
= old
->ip6q_next
;
686 old
->ip6q_next
->ip6q_prev
= new;
687 old
->ip6q_next
= new;
694 p6
->ip6q_prev
->ip6q_next
= p6
->ip6q_next
;
695 p6
->ip6q_next
->ip6q_prev
= p6
->ip6q_prev
;
699 * IPv6 reassembling timer processing;
700 * if a timer expires on a reassembly
707 lck_mtx_lock(inet6_domain_mutex
);
709 frag6_doing_reass
= 1;
712 while (q6
!= &ip6q
) {
715 if (q6
->ip6q_prev
->ip6q_ttl
== 0) {
716 ip6stat
.ip6s_fragtimeout
++;
717 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
718 frag6_freef(q6
->ip6q_prev
);
722 * If we are over the maximum number of fragments
723 * (due to the limit being lowered), drain off
724 * enough to get down to the new limit.
726 while (frag6_nfragpackets
> (u_int
)ip6_maxfragpackets
&&
728 ip6stat
.ip6s_fragoverflow
++;
729 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
730 frag6_freef(ip6q
.ip6q_prev
);
732 frag6_doing_reass
= 0;
733 lck_mtx_unlock(inet6_domain_mutex
);
737 * Drain off all datagram fragments.
742 if (frag6_doing_reass
)
744 lck_mtx_lock(inet6_domain_mutex
);
745 while (ip6q
.ip6q_next
!= &ip6q
) {
746 ip6stat
.ip6s_fragdropped
++;
747 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
748 frag6_freef(ip6q
.ip6q_next
);
750 lck_mtx_unlock(inet6_domain_mutex
);