]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/frag6.c
xnu-1504.9.37.tar.gz
[apple/xnu.git] / bsd / netinet6 / frag6.c
CommitLineData
b0d623f7
A
1/*
2 * Copyright (c) 2008 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
9bccf70c
A
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 $ */
1c79356b
A
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/mbuf.h>
65#include <sys/domain.h>
66#include <sys/protosw.h>
67#include <sys/socket.h>
68#include <sys/errno.h>
69#include <sys/time.h>
70#include <sys/kernel.h>
71#include <sys/syslog.h>
72#include <kern/queue.h>
91447636 73#include <kern/locks.h>
1c79356b
A
74
75#include <net/if.h>
76#include <net/route.h>
77
78#include <netinet/in.h>
79#include <netinet/in_var.h>
80#include <netinet/ip6.h>
81#include <netinet6/ip6_var.h>
1c79356b
A
82#include <netinet/icmp6.h>
83
84#include <net/net_osdep.h>
85
86/*
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.
90 */
91#define IN6_IFSTAT_STRICT
92
91447636
A
93static void frag6_enq(struct ip6asfrag *, struct ip6asfrag *);
94static void frag6_deq(struct ip6asfrag *);
95static void frag6_insque(struct ip6q *, struct ip6q *);
96static void frag6_remque(struct ip6q *);
97static void frag6_freef(struct ip6q *);
1c79356b 98
9bccf70c 99/* XXX we eventually need splreass6, or some real semaphore */
1c79356b
A
100int frag6_doing_reass;
101u_int frag6_nfragpackets;
91447636 102static u_int frag6_nfrags;
1c79356b
A
103struct ip6q ip6q; /* ip6 reassemble queue */
104
9bccf70c 105#ifndef __APPLE__
1c79356b
A
106MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header");
107#endif
108
91447636 109extern lck_mtx_t *inet6_domain_mutex;
1c79356b
A
110/*
111 * Initialise reassembly queue and fragment identifier.
112 */
113void
114frag6_init()
115{
116 struct timeval tv;
117
483a1d10 118 ip6_maxfragpackets = nmbclusters / 32;
91447636 119 ip6_maxfrags = nmbclusters / 4;
9bccf70c 120
1c79356b
A
121 /*
122 * in many cases, random() here does NOT return random number
123 * as initialization during bootstrap time occur in fixed order.
124 */
125 microtime(&tv);
1c79356b 126 ip6_id = random() ^ tv.tv_usec;
9bccf70c 127 ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
1c79356b
A
128}
129
130/*
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.
135 *
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
141 *
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
147 *
148 * The following note also contradicts with fragment rule - noone is going to
149 * send different fragment with different next header field.
150 *
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
156 *
157 * There is no explicit reason given in the RFC. Historical reason maybe?
158 */
159/*
160 * Fragment input
91447636
A
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.
1c79356b
A
163 */
164int
55e303ae 165frag6_input(mp, offp)
1c79356b 166 struct mbuf **mp;
55e303ae 167 int *offp;
1c79356b
A
168{
169 struct mbuf *m = *mp, *t;
170 struct ip6_hdr *ip6;
171 struct ip6_frag *ip6f;
172 struct ip6q *q6;
173 struct ip6asfrag *af6, *ip6af, *af6dwn;
174 int offset = *offp, nxt, i, next;
175 int first_frag = 0;
176 int fragoff, frgpartlen; /* must be larger than u_int16_t */
177 struct ifnet *dstifp;
b0d623f7 178 struct ifaddr *ifa = NULL;
1c79356b 179#ifdef IN6_IFSTAT_STRICT
b0d623f7 180 struct route_in6 ro;
1c79356b
A
181 struct sockaddr_in6 *dst;
182#endif
183
184 ip6 = mtod(m, struct ip6_hdr *);
185#ifndef PULLDOWN_TEST
91447636 186 IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), return IPPROTO_DONE);
1c79356b
A
187 ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset);
188#else
189 IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f));
190 if (ip6f == NULL)
191 return IPPROTO_DONE;
192#endif
193
194 dstifp = NULL;
195#ifdef IN6_IFSTAT_STRICT
196 /* find the destination interface of the packet. */
b0d623f7 197 bzero(&ro, sizeof (ro));
1c79356b 198 dst = (struct sockaddr_in6 *)&ro.ro_dst;
b0d623f7
A
199 dst->sin6_family = AF_INET6;
200 dst->sin6_len = sizeof (struct sockaddr_in6);
201 dst->sin6_addr = ip6->ip6_dst;
202
203 rtalloc((struct route *)&ro);
204 if (ro.ro_rt != NULL) {
205 RT_LOCK(ro.ro_rt);
206 if ((ifa = ro.ro_rt->rt_ifa) != NULL) {
207 ifaref(ifa);
208 dstifp = ((struct in6_ifaddr *)ro.ro_rt->rt_ifa)->ia_ifp;
209 }
210 RT_UNLOCK(ro.ro_rt);
9bccf70c 211 rtfree(ro.ro_rt);
b0d623f7 212 ro.ro_rt = NULL;
1c79356b 213 }
1c79356b
A
214#else
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;
218#endif
219
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);
b0d623f7
A
224 if (ifa != NULL)
225 ifafree(ifa);
1c79356b
A
226 return IPPROTO_DONE;
227 }
228
229 /*
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
234 */
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);
b0d623f7
A
241 if (ifa != NULL)
242 ifafree(ifa);
1c79356b
A
243 return IPPROTO_DONE;
244 }
245
246 ip6stat.ip6s_fragments++;
247 in6_ifstat_inc(dstifp, ifs6_reass_reqd);
248
249 /* offset now points to data portion */
250 offset += sizeof(struct ip6_frag);
251
9bccf70c
A
252 frag6_doing_reass = 1;
253
91447636
A
254 /*
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.
258 */
259 if (ip6_maxfrags < 0)
260 ;
261 else if (frag6_nfrags >= (u_int)ip6_maxfrags)
262 goto dropfrag;
263
1c79356b
A
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))
268 break;
269
270 if (q6 == &ip6q) {
271 /*
272 * the first fragment to arrive, create a reassembly queue.
273 */
274 first_frag = 1;
1c79356b
A
275
276 /*
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.
281 */
9bccf70c
A
282 if (ip6_maxfragpackets < 0)
283 ;
284 else if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets)
285 goto dropfrag;
286 frag6_nfragpackets++;
1c79356b
A
287 q6 = (struct ip6q *)_MALLOC(sizeof(struct ip6q), M_FTABLE,
288 M_DONTWAIT);
289 if (q6 == NULL)
290 goto dropfrag;
291 bzero(q6, sizeof(*q6));
292
293 frag6_insque(q6, &ip6q);
294
295 /* ip6q_nxt will be filled afterwards, from 1st fragment */
296 q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6;
9bccf70c 297#ifdef notyet
1c79356b
A
298 q6->ip6q_nxtp = (u_char *)nxtp;
299#endif
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. */
91447636
A
306
307 q6->ip6q_nfrag = 0;
1c79356b
A
308 }
309
310 /*
311 * If it's the 1st fragment, record the length of the
312 * unfragmentable part and the next header of the fragment header.
313 */
314 fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
315 if (fragoff == 0) {
316 q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr)
317 - sizeof(struct ip6_frag);
318 q6->ip6q_nxt = ip6f->ip6f_nxt;
319 }
320
321 /*
322 * Check that the reassembled packet would not exceed 65535 bytes
323 * in size.
324 * If it would exceed, discard the fragment and return an ICMP error.
325 */
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));
9bccf70c 333 frag6_doing_reass = 0;
b0d623f7
A
334 if (ifa != NULL)
335 ifafree(ifa);
1c79356b
A
336 return(IPPROTO_DONE);
337 }
338 }
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));
9bccf70c 343 frag6_doing_reass = 0;
b0d623f7
A
344 if (ifa != NULL)
345 ifafree(ifa);
1c79356b
A
346 return(IPPROTO_DONE);
347 }
348 /*
349 * If it's the first fragment, do the above check for each
350 * fragment already stored in the reassembly queue.
351 */
352 if (fragoff == 0) {
353 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
354 af6 = af6dwn) {
355 af6dwn = af6->ip6af_down;
356
357 if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen >
358 IPV6_MAXPACKET) {
359 struct mbuf *merr = IP6_REASS_MBUF(af6);
360 struct ip6_hdr *ip6err;
361 int erroff = af6->ip6af_offset;
362
363 /* dequeue the fragment. */
364 frag6_deq(af6);
9bccf70c 365 FREE(af6, M_FTABLE);
1c79356b
A
366
367 /* adjust pointer. */
368 ip6err = mtod(merr, struct ip6_hdr *);
369
370 /*
371 * Restore source and destination addresses
372 * in the erroneous IPv6 header.
373 */
374 ip6err->ip6_src = q6->ip6q_src;
375 ip6err->ip6_dst = q6->ip6q_dst;
376
377 icmp6_error(merr, ICMP6_PARAM_PROB,
378 ICMP6_PARAMPROB_HEADER,
379 erroff - sizeof(struct ip6_frag) +
380 offsetof(struct ip6_frag, ip6f_offlg));
381 }
382 }
383 }
384
385 ip6af = (struct ip6asfrag *)_MALLOC(sizeof(struct ip6asfrag), M_FTABLE,
386 M_DONTWAIT);
387 if (ip6af == NULL)
388 goto dropfrag;
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;
399
400 if (first_frag) {
401 af6 = (struct ip6asfrag *)q6;
402 goto insert;
403 }
404
405 /*
406 * Find a segment which begins after this one does.
407 */
408 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
409 af6 = af6->ip6af_down)
410 if (af6->ip6af_off > ip6af->ip6af_off)
411 break;
412
413#if 0
414 /*
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.
418 */
419 if (af6->ip6af_up != (struct ip6asfrag *)q6) {
420 i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen
421 - ip6af->ip6af_off;
422 if (i > 0) {
423 if (i >= ip6af->ip6af_frglen)
424 goto dropfrag;
425 m_adj(IP6_REASS_MBUF(ip6af), i);
426 ip6af->ip6af_off += i;
427 ip6af->ip6af_frglen -= i;
428 }
429 }
430
431 /*
432 * While we overlap succeeding segments trim them or,
433 * if they are completely covered, dequeue them.
434 */
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;
440 af6->ip6af_off += i;
441 m_adj(IP6_REASS_MBUF(af6), i);
442 break;
443 }
444 af6 = af6->ip6af_down;
445 m_freem(IP6_REASS_MBUF(af6->ip6af_up));
446 frag6_deq(af6->ip6af_up);
447 }
448#else
449 /*
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.
453 */
454 if (af6->ip6af_up != (struct ip6asfrag *)q6) {
455 i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen
456 - ip6af->ip6af_off;
457 if (i > 0) {
9bccf70c 458#if 0 /* suppress the noisy log */
1c79356b
A
459 log(LOG_ERR, "%d bytes of a fragment from %s "
460 "overlaps the previous fragment\n",
461 i, ip6_sprintf(&q6->ip6q_src));
9bccf70c
A
462#endif
463 FREE(ip6af, M_FTABLE);
1c79356b
A
464 goto dropfrag;
465 }
466 }
467 if (af6 != (struct ip6asfrag *)q6) {
468 i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off;
469 if (i > 0) {
9bccf70c 470#if 0 /* suppress the noisy log */
1c79356b
A
471 log(LOG_ERR, "%d bytes of a fragment from %s "
472 "overlaps the succeeding fragment",
473 i, ip6_sprintf(&q6->ip6q_src));
9bccf70c
A
474#endif
475 FREE(ip6af, M_FTABLE);
1c79356b
A
476 goto dropfrag;
477 }
478 }
479#endif
480
481insert:
482
483 /*
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.
488 */
489 frag6_enq(ip6af, af6->ip6af_up);
91447636
A
490 frag6_nfrags++;
491 q6->ip6q_nfrag++;
1c79356b
A
492#if 0 /* xxx */
493 if (q6 != ip6q.ip6q_next) {
494 frag6_remque(q6);
495 frag6_insque(q6, &ip6q);
496 }
497#endif
498 next = 0;
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;
b0d623f7
A
503 if (ifa != NULL)
504 ifafree(ifa);
1c79356b
A
505 return IPPROTO_DONE;
506 }
507 next += af6->ip6af_frglen;
508 }
509 if (af6->ip6af_up->ip6af_mff) {
510 frag6_doing_reass = 0;
b0d623f7
A
511 if (ifa != NULL)
512 ifafree(ifa);
1c79356b
A
513 return IPPROTO_DONE;
514 }
515
516 /*
517 * Reassembly is complete; concatenate fragments.
518 */
519 ip6af = q6->ip6q_down;
520 t = m = IP6_REASS_MBUF(ip6af);
521 af6 = ip6af->ip6af_down;
522 frag6_deq(ip6af);
523 while (af6 != (struct ip6asfrag *)q6) {
524 af6dwn = af6->ip6af_down;
525 frag6_deq(af6);
526 while (t->m_next)
527 t = t->m_next;
528 t->m_next = IP6_REASS_MBUF(af6);
529 m_adj(t->m_next, af6->ip6af_offset);
9bccf70c 530 FREE(af6, M_FTABLE);
1c79356b
A
531 af6 = af6dwn;
532 }
533
534 /* adjust offset to point where the original next header starts */
535 offset = ip6af->ip6af_offset - sizeof(struct ip6_frag);
9bccf70c 536 FREE(ip6af, M_FTABLE);
1c79356b
A
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;
541 nxt = q6->ip6q_nxt;
542#if notyet
543 *q6->ip6q_nxtp = (u_char)(nxt & 0xff);
544#endif
545
546 /*
547 * Delete frag6 header with as a few cost as possible.
548 */
549 if (offset < m->m_len) {
550 ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag),
551 offset);
552 m->m_data += sizeof(struct ip6_frag);
553 m->m_len -= sizeof(struct ip6_frag);
554 } else {
555 /* this comes with no copy if the boundary is on cluster */
556 if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) {
557 frag6_remque(q6);
91447636 558 frag6_nfrags -= q6->ip6q_nfrag;
9bccf70c 559 FREE(q6, M_FTABLE);
1c79356b
A
560 frag6_nfragpackets--;
561 goto dropfrag;
562 }
563 m_adj(t, sizeof(struct ip6_frag));
564 m_cat(m, t);
565 }
566
567 /*
568 * Store NXT to the original.
569 */
570 {
571 char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */
572 *prvnxtp = nxt;
573 }
574
575 frag6_remque(q6);
91447636 576 frag6_nfrags -= q6->ip6q_nfrag;
9bccf70c 577 FREE(q6, M_FTABLE);
1c79356b
A
578 frag6_nfragpackets--;
579
580 if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */
581 int plen = 0;
582 for (t = m; t; t = t->m_next)
583 plen += t->m_len;
584 m->m_pkthdr.len = plen;
585 }
586
587 ip6stat.ip6s_reassembled++;
588 in6_ifstat_inc(dstifp, ifs6_reass_ok);
589
590 /*
591 * Tell launch routine the next header
592 */
593
594 *mp = m;
595 *offp = offset;
596
597 frag6_doing_reass = 0;
b0d623f7
A
598 if (ifa != NULL)
599 ifafree(ifa);
1c79356b
A
600 return nxt;
601
602 dropfrag:
603 in6_ifstat_inc(dstifp, ifs6_reass_fail);
604 ip6stat.ip6s_fragdropped++;
605 m_freem(m);
9bccf70c 606 frag6_doing_reass = 0;
b0d623f7
A
607 if (ifa != NULL)
608 ifafree(ifa);
1c79356b
A
609 return IPPROTO_DONE;
610}
611
612/*
613 * Free a fragment reassembly header and all
614 * associated datagrams.
615 */
616void
617frag6_freef(q6)
618 struct ip6q *q6;
619{
620 struct ip6asfrag *af6, *down6;
621
622 for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
623 af6 = down6) {
624 struct mbuf *m = IP6_REASS_MBUF(af6);
625
626 down6 = af6->ip6af_down;
627 frag6_deq(af6);
628
629 /*
630 * Return ICMP time exceeded error for the 1st fragment.
631 * Just free other fragments.
632 */
633 if (af6->ip6af_off == 0) {
634 struct ip6_hdr *ip6;
635
636 /* adjust pointer */
637 ip6 = mtod(m, struct ip6_hdr *);
638
639 /* restoure source and destination addresses */
640 ip6->ip6_src = q6->ip6q_src;
641 ip6->ip6_dst = q6->ip6q_dst;
1c79356b
A
642 icmp6_error(m, ICMP6_TIME_EXCEEDED,
643 ICMP6_TIME_EXCEED_REASSEMBLY, 0);
644 } else
645 m_freem(m);
9bccf70c 646 FREE(af6, M_FTABLE);
1c79356b
A
647
648 }
649 frag6_remque(q6);
91447636 650 frag6_nfrags -= q6->ip6q_nfrag;
9bccf70c 651 FREE(q6, M_FTABLE);
1c79356b
A
652 frag6_nfragpackets--;
653}
654
655/*
656 * Put an ip fragment on a reassembly chain.
657 * Like insque, but pointers in middle of structure.
658 */
659void
660frag6_enq(af6, up6)
661 struct ip6asfrag *af6, *up6;
662{
663 af6->ip6af_up = up6;
664 af6->ip6af_down = up6->ip6af_down;
665 up6->ip6af_down->ip6af_up = af6;
666 up6->ip6af_down = af6;
667}
668
669/*
670 * To frag6_enq as remque is to insque.
671 */
672void
673frag6_deq(af6)
674 struct ip6asfrag *af6;
675{
676 af6->ip6af_up->ip6af_down = af6->ip6af_down;
677 af6->ip6af_down->ip6af_up = af6->ip6af_up;
678}
679
680void
681frag6_insque(new, old)
682 struct ip6q *new, *old;
683{
684 new->ip6q_prev = old;
685 new->ip6q_next = old->ip6q_next;
686 old->ip6q_next->ip6q_prev= new;
687 old->ip6q_next = new;
688}
689
690void
691frag6_remque(p6)
692 struct ip6q *p6;
693{
694 p6->ip6q_prev->ip6q_next = p6->ip6q_next;
695 p6->ip6q_next->ip6q_prev = p6->ip6q_prev;
696}
697
698/*
9bccf70c 699 * IPv6 reassembling timer processing;
1c79356b
A
700 * if a timer expires on a reassembly
701 * queue, discard it.
702 */
703void
704frag6_slowtimo()
705{
706 struct ip6q *q6;
91447636 707 lck_mtx_lock(inet6_domain_mutex);
1c79356b
A
708
709 frag6_doing_reass = 1;
710 q6 = ip6q.ip6q_next;
711 if (q6)
712 while (q6 != &ip6q) {
713 --q6->ip6q_ttl;
714 q6 = q6->ip6q_next;
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);
719 }
720 }
721 /*
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.
725 */
9bccf70c
A
726 while (frag6_nfragpackets > (u_int)ip6_maxfragpackets &&
727 ip6q.ip6q_prev) {
1c79356b
A
728 ip6stat.ip6s_fragoverflow++;
729 /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
730 frag6_freef(ip6q.ip6q_prev);
731 }
732 frag6_doing_reass = 0;
91447636 733 lck_mtx_unlock(inet6_domain_mutex);
1c79356b
A
734}
735
736/*
737 * Drain off all datagram fragments.
738 */
739void
740frag6_drain()
741{
742 if (frag6_doing_reass)
743 return;
91447636 744 lck_mtx_lock(inet6_domain_mutex);
1c79356b
A
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);
749 }
91447636 750 lck_mtx_unlock(inet6_domain_mutex);
1c79356b 751}