1*4eca214dSbluhm /* $OpenBSD: frag6.c,v 1.89 2024/07/29 12:41:30 bluhm Exp $ */ 2363d560cSitojun /* $KAME: frag6.c,v 1.40 2002/05/27 21:40:31 itojun Exp $ */ 3287546eaSitojun 4287546eaSitojun /* 5287546eaSitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6287546eaSitojun * All rights reserved. 7287546eaSitojun * 8287546eaSitojun * Redistribution and use in source and binary forms, with or without 9287546eaSitojun * modification, are permitted provided that the following conditions 10287546eaSitojun * are met: 11287546eaSitojun * 1. Redistributions of source code must retain the above copyright 12287546eaSitojun * notice, this list of conditions and the following disclaimer. 13287546eaSitojun * 2. Redistributions in binary form must reproduce the above copyright 14287546eaSitojun * notice, this list of conditions and the following disclaimer in the 15287546eaSitojun * documentation and/or other materials provided with the distribution. 16287546eaSitojun * 3. Neither the name of the project nor the names of its contributors 17287546eaSitojun * may be used to endorse or promote products derived from this software 18287546eaSitojun * without specific prior written permission. 19287546eaSitojun * 20287546eaSitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21287546eaSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22287546eaSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23287546eaSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24287546eaSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25287546eaSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26287546eaSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27287546eaSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28287546eaSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29287546eaSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30287546eaSitojun * SUCH DAMAGE. 31287546eaSitojun */ 32287546eaSitojun 33287546eaSitojun #include <sys/param.h> 34287546eaSitojun #include <sys/systm.h> 35287546eaSitojun #include <sys/mbuf.h> 36287546eaSitojun #include <sys/socket.h> 37287546eaSitojun #include <sys/errno.h> 38287546eaSitojun #include <sys/time.h> 39287546eaSitojun #include <sys/kernel.h> 40c91462b0Svisa #include <sys/pool.h> 41142f92b0Svisa #include <sys/mutex.h> 42287546eaSitojun 43287546eaSitojun #include <net/if.h> 440deb6685Smpi #include <net/if_var.h> 45287546eaSitojun #include <net/route.h> 46287546eaSitojun 47287546eaSitojun #include <netinet/in.h> 48dc572864Sbluhm #include <netinet6/in6_var.h> 49fa86ee14Sitojun #include <netinet/ip6.h> 50287546eaSitojun #include <netinet6/ip6_var.h> 51fa86ee14Sitojun #include <netinet/icmp6.h> 5274e8fb40Skjc #include <netinet/ip.h> /* for ECN definitions */ 53287546eaSitojun 54142f92b0Svisa /* Protects `frag6_queue', `frag6_nfragpackets' and `frag6_nfrags'. */ 55142f92b0Svisa struct mutex frag6_mutex = MUTEX_INITIALIZER(IPL_SOFTNET); 56287546eaSitojun 57287546eaSitojun u_int frag6_nfragpackets; 58363d560cSitojun u_int frag6_nfrags; 592061f085Sbluhm TAILQ_HEAD(ip6q_head, ip6q) frag6_queue; /* ip6 reassemble queue */ 60287546eaSitojun 61142f92b0Svisa void frag6_freef(struct ip6q *); 62142f92b0Svisa void frag6_unlink(struct ip6q *, struct ip6q_head *); 63142f92b0Svisa 64c91462b0Svisa struct pool ip6af_pool; 65c91462b0Svisa struct pool ip6q_pool; 66c91462b0Svisa 67287546eaSitojun /* 68c91462b0Svisa * Initialise reassembly queue and pools. 69287546eaSitojun */ 70287546eaSitojun void 71a0aa363cSjsing frag6_init(void) 72287546eaSitojun { 73c91462b0Svisa pool_init(&ip6af_pool, sizeof(struct ip6asfrag), 74c91462b0Svisa 0, IPL_SOFTNET, 0, "ip6af", NULL); 75c91462b0Svisa pool_init(&ip6q_pool, sizeof(struct ip6q), 76c91462b0Svisa 0, IPL_SOFTNET, 0, "ip6q", NULL); 77d8b2e91cSitojun 782061f085Sbluhm TAILQ_INIT(&frag6_queue); 79287546eaSitojun } 80287546eaSitojun 81287546eaSitojun /* 82813d632dSitojun * In RFC2460, fragment and reassembly rule do not agree with each other, 83813d632dSitojun * in terms of next header field handling in fragment header. 84813d632dSitojun * While the sender will use the same value for all of the fragmented packets, 85813d632dSitojun * receiver is suggested not to check the consistency. 86813d632dSitojun * 87813d632dSitojun * fragment rule (p20): 88813d632dSitojun * (2) A Fragment header containing: 89813d632dSitojun * The Next Header value that identifies the first header of 90813d632dSitojun * the Fragmentable Part of the original packet. 91813d632dSitojun * -> next header field is same for all fragments 92813d632dSitojun * 93813d632dSitojun * reassembly rule (p21): 94813d632dSitojun * The Next Header field of the last header of the Unfragmentable 95813d632dSitojun * Part is obtained from the Next Header field of the first 96813d632dSitojun * fragment's Fragment header. 97813d632dSitojun * -> should grab it from the first fragment only 98813d632dSitojun * 99813d632dSitojun * The following note also contradicts with fragment rule - noone is going to 100813d632dSitojun * send different fragment with different next header field. 101813d632dSitojun * 102813d632dSitojun * additional note (p22): 103813d632dSitojun * The Next Header values in the Fragment headers of different 104813d632dSitojun * fragments of the same original packet may differ. Only the value 105813d632dSitojun * from the Offset zero fragment packet is used for reassembly. 106813d632dSitojun * -> should grab it from the first fragment only 107813d632dSitojun * 108813d632dSitojun * There is no explicit reason given in the RFC. Historical reason maybe? 109813d632dSitojun */ 110813d632dSitojun /* 111287546eaSitojun * Fragment input 112287546eaSitojun */ 113287546eaSitojun int 114459fa0feSbluhm frag6_input(struct mbuf **mp, int *offp, int proto, int af) 115287546eaSitojun { 116287546eaSitojun struct mbuf *m = *mp, *t; 117287546eaSitojun struct ip6_hdr *ip6; 118287546eaSitojun struct ip6_frag *ip6f; 119287546eaSitojun struct ip6q *q6; 120068c59a8Sbluhm struct ip6asfrag *af6, *ip6af, *naf6, *paf6; 121287546eaSitojun int offset = *offp, nxt, i, next; 122287546eaSitojun int first_frag = 0; 123813d632dSitojun int fragoff, frgpartlen; /* must be larger than u_int16_t */ 12474e8fb40Skjc u_int8_t ecn, ecn0; 125287546eaSitojun 126287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 127287546eaSitojun IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); 128287546eaSitojun if (ip6f == NULL) 129287546eaSitojun return IPPROTO_DONE; 130287546eaSitojun 131287546eaSitojun /* jumbo payload can't contain a fragment header */ 132287546eaSitojun if (ip6->ip6_plen == 0) { 133*4eca214dSbluhm icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 134*4eca214dSbluhm offset); 135287546eaSitojun return IPPROTO_DONE; 136287546eaSitojun } 137287546eaSitojun 138287546eaSitojun /* 139287546eaSitojun * check whether fragment packet's fragment length is 140287546eaSitojun * multiple of 8 octets. 141287546eaSitojun * sizeof(struct ip6_frag) == 8 142287546eaSitojun * sizeof(struct ip6_hdr) = 40 143287546eaSitojun */ 144287546eaSitojun if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && 145287546eaSitojun (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { 146363d560cSitojun icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 147813d632dSitojun offsetof(struct ip6_hdr, ip6_plen)); 148287546eaSitojun return IPPROTO_DONE; 149287546eaSitojun } 150287546eaSitojun 15131e14cacSjca ip6stat_inc(ip6s_fragments); 152287546eaSitojun 153813d632dSitojun /* offset now points to data portion */ 154287546eaSitojun offset += sizeof(struct ip6_frag); 155287546eaSitojun 156158c6e5aSbluhm /* 157ed22750fSmpi * RFC6946: A host that receives an IPv6 packet which includes 158ed22750fSmpi * a Fragment Header with the "Fragment Offset" equal to 0 and 159ed22750fSmpi * the "M" bit equal to 0 MUST process such packet in isolation 160ed22750fSmpi * from any other packets/fragments. 161158c6e5aSbluhm */ 162158c6e5aSbluhm fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); 163158c6e5aSbluhm if (fragoff == 0 && !(ip6f->ip6f_offlg & IP6F_MORE_FRAG)) { 16431e14cacSjca ip6stat_inc(ip6s_reassembled); 165158c6e5aSbluhm *offp = offset; 166158c6e5aSbluhm return ip6f->ip6f_nxt; 167158c6e5aSbluhm } 168158c6e5aSbluhm 16901402e93Sbluhm /* Ignore empty non atomic fragment, do not classify as overlapping. */ 17001402e93Sbluhm if (sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) <= offset) { 17101402e93Sbluhm m_freem(m); 17201402e93Sbluhm return IPPROTO_DONE; 17301402e93Sbluhm } 17401402e93Sbluhm 175142f92b0Svisa mtx_enter(&frag6_mutex); 1762b0c183cSitojun 177363d560cSitojun /* 178363d560cSitojun * Enforce upper bound on number of fragments. 179363d560cSitojun * If maxfrag is 0, never accept fragments. 180363d560cSitojun * If maxfrag is -1, accept all fragments without limitation. 181363d560cSitojun */ 182142f92b0Svisa if (ip6_maxfrags >= 0 && frag6_nfrags >= (u_int)ip6_maxfrags) { 183142f92b0Svisa mtx_leave(&frag6_mutex); 184363d560cSitojun goto dropfrag; 185142f92b0Svisa } 186363d560cSitojun 1872061f085Sbluhm TAILQ_FOREACH(q6, &frag6_queue, ip6q_queue) 188287546eaSitojun if (ip6f->ip6f_ident == q6->ip6q_ident && 189287546eaSitojun IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && 190287546eaSitojun IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst)) 191287546eaSitojun break; 192287546eaSitojun 19366c84f52Stedu if (q6 == NULL) { 194287546eaSitojun /* 195287546eaSitojun * the first fragment to arrive, create a reassembly queue. 196287546eaSitojun */ 197287546eaSitojun first_frag = 1; 198287546eaSitojun 199287546eaSitojun /* 200287546eaSitojun * Enforce upper bound on number of fragmented packets 201287546eaSitojun * for which we attempt reassembly; 202363d560cSitojun * If maxfragpackets is 0, never accept fragments. 203363d560cSitojun * If maxfragpackets is -1, accept all fragments without 204363d560cSitojun * limitation. 205287546eaSitojun */ 2062efa9c30Stedu if (ip6_maxfragpackets >= 0 && 207142f92b0Svisa frag6_nfragpackets >= (u_int)ip6_maxfragpackets) { 208142f92b0Svisa mtx_leave(&frag6_mutex); 20918214350Sitojun goto dropfrag; 210142f92b0Svisa } 21118214350Sitojun frag6_nfragpackets++; 212c91462b0Svisa q6 = pool_get(&ip6q_pool, PR_NOWAIT | PR_ZERO); 213142f92b0Svisa if (q6 == NULL) { 214142f92b0Svisa mtx_leave(&frag6_mutex); 215287546eaSitojun goto dropfrag; 216142f92b0Svisa } 217287546eaSitojun 2182061f085Sbluhm TAILQ_INSERT_HEAD(&frag6_queue, q6, ip6q_queue); 219287546eaSitojun 220813d632dSitojun /* ip6q_nxt will be filled afterwards, from 1st fragment */ 221068c59a8Sbluhm LIST_INIT(&q6->ip6q_asfrag); 222287546eaSitojun q6->ip6q_ident = ip6f->ip6f_ident; 223287546eaSitojun q6->ip6q_ttl = IPV6_FRAGTTL; 224287546eaSitojun q6->ip6q_src = ip6->ip6_src; 225287546eaSitojun q6->ip6q_dst = ip6->ip6_dst; 226d3f3cb99Sbluhm q6->ip6q_ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; 227287546eaSitojun q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ 228363d560cSitojun q6->ip6q_nfrag = 0; 229287546eaSitojun } 230287546eaSitojun 231287546eaSitojun /* 232287546eaSitojun * If it's the 1st fragment, record the length of the 233287546eaSitojun * unfragmentable part and the next header of the fragment header. 234287546eaSitojun */ 235287546eaSitojun if (fragoff == 0) { 236363d560cSitojun q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) - 237363d560cSitojun sizeof(struct ip6_frag); 238287546eaSitojun q6->ip6q_nxt = ip6f->ip6f_nxt; 239287546eaSitojun } 240287546eaSitojun 241287546eaSitojun /* 242287546eaSitojun * Check that the reassembled packet would not exceed 65535 bytes 243287546eaSitojun * in size. 244287546eaSitojun * If it would exceed, discard the fragment and return an ICMP error. 245287546eaSitojun */ 246287546eaSitojun frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; 247287546eaSitojun if (q6->ip6q_unfrglen >= 0) { 248287546eaSitojun /* The 1st fragment has already arrived. */ 249287546eaSitojun if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) { 250142f92b0Svisa mtx_leave(&frag6_mutex); 251287546eaSitojun icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 252813d632dSitojun offset - sizeof(struct ip6_frag) + 253813d632dSitojun offsetof(struct ip6_frag, ip6f_offlg)); 254287546eaSitojun return (IPPROTO_DONE); 255287546eaSitojun } 256363d560cSitojun } else if (fragoff + frgpartlen > IPV6_MAXPACKET) { 257142f92b0Svisa mtx_leave(&frag6_mutex); 258287546eaSitojun icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 259813d632dSitojun offset - sizeof(struct ip6_frag) + 260813d632dSitojun offsetof(struct ip6_frag, ip6f_offlg)); 261287546eaSitojun return (IPPROTO_DONE); 262287546eaSitojun } 263287546eaSitojun /* 264287546eaSitojun * If it's the first fragment, do the above check for each 265287546eaSitojun * fragment already stored in the reassembly queue. 266287546eaSitojun */ 267287546eaSitojun if (fragoff == 0) { 268068c59a8Sbluhm LIST_FOREACH_SAFE(af6, &q6->ip6q_asfrag, ip6af_list, naf6) { 269068c59a8Sbluhm if (q6->ip6q_unfrglen + af6->ip6af_off + 270068c59a8Sbluhm af6->ip6af_frglen > IPV6_MAXPACKET) { 27170756ee2Svisa struct mbuf *merr = af6->ip6af_m; 272287546eaSitojun struct ip6_hdr *ip6err; 273287546eaSitojun int erroff = af6->ip6af_offset; 274287546eaSitojun 275287546eaSitojun /* dequeue the fragment. */ 276068c59a8Sbluhm LIST_REMOVE(af6, ip6af_list); 277c91462b0Svisa pool_put(&ip6af_pool, af6); 278287546eaSitojun 279287546eaSitojun /* adjust pointer. */ 280287546eaSitojun ip6err = mtod(merr, struct ip6_hdr *); 281287546eaSitojun 282287546eaSitojun /* 283287546eaSitojun * Restore source and destination addresses 284287546eaSitojun * in the erroneous IPv6 header. 285287546eaSitojun */ 286287546eaSitojun ip6err->ip6_src = q6->ip6q_src; 287287546eaSitojun ip6err->ip6_dst = q6->ip6q_dst; 288287546eaSitojun 289287546eaSitojun icmp6_error(merr, ICMP6_PARAM_PROB, 290287546eaSitojun ICMP6_PARAMPROB_HEADER, 291813d632dSitojun erroff - sizeof(struct ip6_frag) + 292813d632dSitojun offsetof(struct ip6_frag, ip6f_offlg)); 293287546eaSitojun } 294287546eaSitojun } 295287546eaSitojun } 296287546eaSitojun 297c91462b0Svisa ip6af = pool_get(&ip6af_pool, PR_NOWAIT | PR_ZERO); 298142f92b0Svisa if (ip6af == NULL) { 299142f92b0Svisa mtx_leave(&frag6_mutex); 300813d632dSitojun goto dropfrag; 301142f92b0Svisa } 302287546eaSitojun ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; 303287546eaSitojun ip6af->ip6af_off = fragoff; 304287546eaSitojun ip6af->ip6af_frglen = frgpartlen; 305287546eaSitojun ip6af->ip6af_offset = offset; 30670756ee2Svisa ip6af->ip6af_m = m; 307287546eaSitojun 308287546eaSitojun if (first_frag) { 30966c84f52Stedu paf6 = NULL; 310287546eaSitojun goto insert; 311287546eaSitojun } 312287546eaSitojun 313287546eaSitojun /* 31474e8fb40Skjc * Handle ECN by comparing this segment with the first one; 31574e8fb40Skjc * if CE is set, do not lose CE. 31674e8fb40Skjc * drop if CE and not-ECT are mixed for the same packet. 31774e8fb40Skjc */ 31874e8fb40Skjc ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; 319d3f3cb99Sbluhm ecn0 = q6->ip6q_ecn; 32074e8fb40Skjc if (ecn == IPTOS_ECN_CE) { 32174e8fb40Skjc if (ecn0 == IPTOS_ECN_NOTECT) { 322142f92b0Svisa mtx_leave(&frag6_mutex); 323c91462b0Svisa pool_put(&ip6af_pool, ip6af); 32474e8fb40Skjc goto dropfrag; 32574e8fb40Skjc } 32674e8fb40Skjc if (ecn0 != IPTOS_ECN_CE) 327d3f3cb99Sbluhm q6->ip6q_ecn = IPTOS_ECN_CE; 32874e8fb40Skjc } 32974e8fb40Skjc if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT) { 330142f92b0Svisa mtx_leave(&frag6_mutex); 331c91462b0Svisa pool_put(&ip6af_pool, ip6af); 33274e8fb40Skjc goto dropfrag; 33374e8fb40Skjc } 33474e8fb40Skjc 33574e8fb40Skjc /* 336287546eaSitojun * Find a segment which begins after this one does. 337287546eaSitojun */ 33866c84f52Stedu for (paf6 = NULL, af6 = LIST_FIRST(&q6->ip6q_asfrag); 33966c84f52Stedu af6 != NULL; 340068c59a8Sbluhm paf6 = af6, af6 = LIST_NEXT(af6, ip6af_list)) 341287546eaSitojun if (af6->ip6af_off > ip6af->ip6af_off) 342287546eaSitojun break; 343287546eaSitojun 344287546eaSitojun /* 345cd457656Sbluhm * RFC 5722, Errata 3089: When reassembling an IPv6 datagram, if one 346cd457656Sbluhm * or more its constituent fragments is determined to be an overlapping 347cd457656Sbluhm * fragment, the entire datagram (and any constituent fragments) MUST 348cd457656Sbluhm * be silently discarded. 349287546eaSitojun */ 35066c84f52Stedu if (paf6 != NULL) { 351068c59a8Sbluhm i = (paf6->ip6af_off + paf6->ip6af_frglen) - ip6af->ip6af_off; 352e09b0f38Smpi if (i > 0) 353c612ca27Sbluhm goto flushfrags; 354287546eaSitojun } 35566c84f52Stedu if (af6 != NULL) { 356287546eaSitojun i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; 357e09b0f38Smpi if (i > 0) 358c612ca27Sbluhm goto flushfrags; 359287546eaSitojun } 360287546eaSitojun 361287546eaSitojun insert: 362287546eaSitojun /* 363287546eaSitojun * Stick new segment in its place; 364287546eaSitojun * check for complete reassembly. 365287546eaSitojun * Move to front of packet queue, as we are 366287546eaSitojun * the most recently active fragmented packet. 367287546eaSitojun */ 36866c84f52Stedu if (paf6 != NULL) 369068c59a8Sbluhm LIST_INSERT_AFTER(paf6, ip6af, ip6af_list); 370068c59a8Sbluhm else 371068c59a8Sbluhm LIST_INSERT_HEAD(&q6->ip6q_asfrag, ip6af, ip6af_list); 372363d560cSitojun frag6_nfrags++; 373363d560cSitojun q6->ip6q_nfrag++; 374287546eaSitojun next = 0; 37566c84f52Stedu for (paf6 = NULL, af6 = LIST_FIRST(&q6->ip6q_asfrag); 37666c84f52Stedu af6 != NULL; 377068c59a8Sbluhm paf6 = af6, af6 = LIST_NEXT(af6, ip6af_list)) { 378287546eaSitojun if (af6->ip6af_off != next) { 379142f92b0Svisa mtx_leave(&frag6_mutex); 380287546eaSitojun return IPPROTO_DONE; 381287546eaSitojun } 382287546eaSitojun next += af6->ip6af_frglen; 383287546eaSitojun } 384068c59a8Sbluhm if (paf6->ip6af_mff) { 385142f92b0Svisa mtx_leave(&frag6_mutex); 386287546eaSitojun return IPPROTO_DONE; 387287546eaSitojun } 388287546eaSitojun 389287546eaSitojun /* 390287546eaSitojun * Reassembly is complete; concatenate fragments. 391287546eaSitojun */ 392068c59a8Sbluhm ip6af = LIST_FIRST(&q6->ip6q_asfrag); 393068c59a8Sbluhm LIST_REMOVE(ip6af, ip6af_list); 39470756ee2Svisa t = m = ip6af->ip6af_m; 39566c84f52Stedu while ((af6 = LIST_FIRST(&q6->ip6q_asfrag)) != NULL) { 396068c59a8Sbluhm LIST_REMOVE(af6, ip6af_list); 397287546eaSitojun while (t->m_next) 398287546eaSitojun t = t->m_next; 39970756ee2Svisa t->m_next = af6->ip6af_m; 400813d632dSitojun m_adj(t->m_next, af6->ip6af_offset); 401c1746f11Sbluhm m_removehdr(t->m_next); 402c91462b0Svisa pool_put(&ip6af_pool, af6); 403287546eaSitojun } 404287546eaSitojun 405287546eaSitojun /* adjust offset to point where the original next header starts */ 406287546eaSitojun offset = ip6af->ip6af_offset - sizeof(struct ip6_frag); 407c91462b0Svisa pool_put(&ip6af_pool, ip6af); 40800dd3bb2Sbluhm next += offset - sizeof(struct ip6_hdr); 40900dd3bb2Sbluhm if ((u_int)next > IPV6_MAXPACKET) { 41000dd3bb2Sbluhm TAILQ_REMOVE(&frag6_queue, q6, ip6q_queue); 41100dd3bb2Sbluhm frag6_nfrags -= q6->ip6q_nfrag; 41200dd3bb2Sbluhm frag6_nfragpackets--; 41300dd3bb2Sbluhm mtx_leave(&frag6_mutex); 41400dd3bb2Sbluhm pool_put(&ip6q_pool, q6); 41500dd3bb2Sbluhm goto dropfrag; 41600dd3bb2Sbluhm } 417813d632dSitojun ip6 = mtod(m, struct ip6_hdr *); 41800dd3bb2Sbluhm ip6->ip6_plen = htons(next); 419287546eaSitojun ip6->ip6_src = q6->ip6q_src; 420287546eaSitojun ip6->ip6_dst = q6->ip6q_dst; 421d3f3cb99Sbluhm if (q6->ip6q_ecn == IPTOS_ECN_CE) 422d3f3cb99Sbluhm ip6->ip6_flow |= htonl(IPTOS_ECN_CE << 20); 423287546eaSitojun nxt = q6->ip6q_nxt; 424287546eaSitojun 4255031a75fSbluhm /* Delete frag6 header */ 426202df550Sbluhm if (frag6_deletefraghdr(m, offset) != 0) { 4272061f085Sbluhm TAILQ_REMOVE(&frag6_queue, q6, ip6q_queue); 428363d560cSitojun frag6_nfrags -= q6->ip6q_nfrag; 429813d632dSitojun frag6_nfragpackets--; 430142f92b0Svisa mtx_leave(&frag6_mutex); 431142f92b0Svisa pool_put(&ip6q_pool, q6); 432813d632dSitojun goto dropfrag; 433287546eaSitojun } 434287546eaSitojun 4352061f085Sbluhm TAILQ_REMOVE(&frag6_queue, q6, ip6q_queue); 436363d560cSitojun frag6_nfrags -= q6->ip6q_nfrag; 437287546eaSitojun frag6_nfragpackets--; 438287546eaSitojun 439142f92b0Svisa mtx_leave(&frag6_mutex); 440142f92b0Svisa 441142f92b0Svisa pool_put(&ip6q_pool, q6); 442142f92b0Svisa 443f02cb2e8Sbluhm m_calchdrlen(m); 444287546eaSitojun 4458398ca50Sbluhm /* 4468398ca50Sbluhm * Restore NXT to the original. 4478398ca50Sbluhm */ 4488398ca50Sbluhm { 4498398ca50Sbluhm int prvnxt = ip6_get_prevhdr(m, offset); 4508398ca50Sbluhm uint8_t *prvnxtp; 4518398ca50Sbluhm 4528398ca50Sbluhm IP6_EXTHDR_GET(prvnxtp, uint8_t *, m, prvnxt, 4538398ca50Sbluhm sizeof(*prvnxtp)); 4548398ca50Sbluhm if (prvnxtp == NULL) 4558398ca50Sbluhm goto dropfrag; 4568398ca50Sbluhm *prvnxtp = nxt; 4578398ca50Sbluhm } 4588398ca50Sbluhm 45931e14cacSjca ip6stat_inc(ip6s_reassembled); 460287546eaSitojun 461287546eaSitojun /* 462287546eaSitojun * Tell launch routine the next header 463287546eaSitojun */ 464287546eaSitojun 465287546eaSitojun *mp = m; 466287546eaSitojun *offp = offset; 467287546eaSitojun 468287546eaSitojun return nxt; 469287546eaSitojun 470c612ca27Sbluhm flushfrags: 471142f92b0Svisa TAILQ_REMOVE(&frag6_queue, q6, ip6q_queue); 472142f92b0Svisa frag6_nfrags -= q6->ip6q_nfrag; 473142f92b0Svisa frag6_nfragpackets--; 474142f92b0Svisa 475142f92b0Svisa mtx_leave(&frag6_mutex); 476142f92b0Svisa 477142f92b0Svisa pool_put(&ip6af_pool, ip6af); 478142f92b0Svisa 47966c84f52Stedu while ((af6 = LIST_FIRST(&q6->ip6q_asfrag)) != NULL) { 480c612ca27Sbluhm LIST_REMOVE(af6, ip6af_list); 48170756ee2Svisa m_freem(af6->ip6af_m); 482c91462b0Svisa pool_put(&ip6af_pool, af6); 483c612ca27Sbluhm } 484142f92b0Svisa ip6stat_add(ip6s_fragdropped, q6->ip6q_nfrag + 1); 485c91462b0Svisa pool_put(&ip6q_pool, q6); 486142f92b0Svisa m_freem(m); 487142f92b0Svisa return IPPROTO_DONE; 488c612ca27Sbluhm 489287546eaSitojun dropfrag: 49031e14cacSjca ip6stat_inc(ip6s_fragdropped); 491287546eaSitojun m_freem(m); 492287546eaSitojun return IPPROTO_DONE; 493287546eaSitojun } 494287546eaSitojun 495287546eaSitojun /* 496202df550Sbluhm * Delete fragment header after the unfragmentable header portions. 497202df550Sbluhm */ 498202df550Sbluhm int 499202df550Sbluhm frag6_deletefraghdr(struct mbuf *m, int offset) 500202df550Sbluhm { 501202df550Sbluhm struct mbuf *t; 502202df550Sbluhm 503202df550Sbluhm if (m->m_len >= offset + sizeof(struct ip6_frag)) { 5048f51fbe3Sderaadt memmove(mtod(m, caddr_t) + sizeof(struct ip6_frag), 5058f51fbe3Sderaadt mtod(m, caddr_t), offset); 506202df550Sbluhm m->m_data += sizeof(struct ip6_frag); 507202df550Sbluhm m->m_len -= sizeof(struct ip6_frag); 508202df550Sbluhm } else { 509202df550Sbluhm /* this comes with no copy if the boundary is on cluster */ 510202df550Sbluhm if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) 511202df550Sbluhm return (ENOBUFS); 512202df550Sbluhm m_adj(t, sizeof(struct ip6_frag)); 513202df550Sbluhm m_cat(m, t); 514202df550Sbluhm } 515202df550Sbluhm 516202df550Sbluhm return (0); 517202df550Sbluhm } 518202df550Sbluhm 519202df550Sbluhm /* 520287546eaSitojun * Free a fragment reassembly header and all 521287546eaSitojun * associated datagrams. 522142f92b0Svisa * The header must not be in any queue. 523287546eaSitojun */ 524287546eaSitojun void 525ee37ea65Smcbride frag6_freef(struct ip6q *q6) 526287546eaSitojun { 527068c59a8Sbluhm struct ip6asfrag *af6; 528287546eaSitojun 52966c84f52Stedu while ((af6 = LIST_FIRST(&q6->ip6q_asfrag)) != NULL) { 53070756ee2Svisa struct mbuf *m = af6->ip6af_m; 531287546eaSitojun 532068c59a8Sbluhm LIST_REMOVE(af6, ip6af_list); 533287546eaSitojun 534287546eaSitojun /* 535287546eaSitojun * Return ICMP time exceeded error for the 1st fragment. 536287546eaSitojun * Just free other fragments. 537287546eaSitojun */ 538287546eaSitojun if (af6->ip6af_off == 0) { 539287546eaSitojun struct ip6_hdr *ip6; 540287546eaSitojun 541287546eaSitojun /* adjust pointer */ 542287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 543287546eaSitojun 544332506f2Sjasper /* restore source and destination addresses */ 545287546eaSitojun ip6->ip6_src = q6->ip6q_src; 546287546eaSitojun ip6->ip6_dst = q6->ip6q_dst; 547287546eaSitojun 548*4eca214dSbluhm NET_LOCK_SHARED(); 549287546eaSitojun icmp6_error(m, ICMP6_TIME_EXCEEDED, 550287546eaSitojun ICMP6_TIME_EXCEED_REASSEMBLY, 0); 551*4eca214dSbluhm NET_UNLOCK_SHARED(); 552813d632dSitojun } else 553287546eaSitojun m_freem(m); 554c91462b0Svisa pool_put(&ip6af_pool, af6); 555287546eaSitojun } 556c91462b0Svisa pool_put(&ip6q_pool, q6); 557142f92b0Svisa } 558142f92b0Svisa 559142f92b0Svisa /* 560142f92b0Svisa * Unlinks a fragment reassembly header from the reassembly queue 561142f92b0Svisa * and inserts it into a given remove queue. 562142f92b0Svisa */ 563142f92b0Svisa void 564142f92b0Svisa frag6_unlink(struct ip6q *q6, struct ip6q_head *rmq6) 565142f92b0Svisa { 566142f92b0Svisa MUTEX_ASSERT_LOCKED(&frag6_mutex); 567142f92b0Svisa 568142f92b0Svisa TAILQ_REMOVE(&frag6_queue, q6, ip6q_queue); 569142f92b0Svisa TAILQ_INSERT_HEAD(rmq6, q6, ip6q_queue); 570142f92b0Svisa frag6_nfrags -= q6->ip6q_nfrag; 571287546eaSitojun frag6_nfragpackets--; 572287546eaSitojun } 573287546eaSitojun 574287546eaSitojun /* 575841d7adbSitojun * IPv6 reassembling timer processing; 576287546eaSitojun * if a timer expires on a reassembly 577287546eaSitojun * queue, discard it. 578287546eaSitojun */ 579287546eaSitojun void 580a0aa363cSjsing frag6_slowtimo(void) 581287546eaSitojun { 582142f92b0Svisa struct ip6q_head rmq6; 5832061f085Sbluhm struct ip6q *q6, *nq6; 584287546eaSitojun 585142f92b0Svisa TAILQ_INIT(&rmq6); 586b74a9adaSmpi 587142f92b0Svisa mtx_enter(&frag6_mutex); 588142f92b0Svisa 589142f92b0Svisa TAILQ_FOREACH_SAFE(q6, &frag6_queue, ip6q_queue, nq6) { 5902061f085Sbluhm if (--q6->ip6q_ttl == 0) { 59131e14cacSjca ip6stat_inc(ip6s_fragtimeout); 592142f92b0Svisa frag6_unlink(q6, &rmq6); 593142f92b0Svisa } 594287546eaSitojun } 5952061f085Sbluhm 596287546eaSitojun /* 597287546eaSitojun * If we are over the maximum number of fragments 598287546eaSitojun * (due to the limit being lowered), drain off 599287546eaSitojun * enough to get down to the new limit. 600287546eaSitojun */ 60118214350Sitojun while (frag6_nfragpackets > (u_int)ip6_maxfragpackets && 6022061f085Sbluhm !TAILQ_EMPTY(&frag6_queue)) { 60331e14cacSjca ip6stat_inc(ip6s_fragoverflow); 604142f92b0Svisa frag6_unlink(TAILQ_LAST(&frag6_queue, ip6q_head), &rmq6); 605287546eaSitojun } 6069b2f66c9Sflorian 607142f92b0Svisa mtx_leave(&frag6_mutex); 608142f92b0Svisa 609142f92b0Svisa while ((q6 = TAILQ_FIRST(&rmq6)) != NULL) { 610142f92b0Svisa TAILQ_REMOVE(&rmq6, q6, ip6q_queue); 611142f92b0Svisa frag6_freef(q6); 612142f92b0Svisa } 613287546eaSitojun } 614