1caf43b02SWarner Losh /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 482cd038dSYoshinobu Inoue * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 582cd038dSYoshinobu Inoue * All rights reserved. 682cd038dSYoshinobu Inoue * 782cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 882cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 982cd038dSYoshinobu Inoue * are met: 1082cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 1182cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 1282cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 1382cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 1482cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 1582cd038dSYoshinobu Inoue * 3. Neither the name of the project nor the names of its contributors 1682cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 1782cd038dSYoshinobu Inoue * without specific prior written permission. 1882cd038dSYoshinobu Inoue * 1982cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2082cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2182cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2282cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2382cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2482cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2582cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2682cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2782cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2882cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2982cd038dSYoshinobu Inoue * SUCH DAMAGE. 30b48287a3SDavid E. O'Brien * 31b48287a3SDavid E. O'Brien * $KAME: frag6.c,v 1.33 2002/01/07 11:34:48 kjc Exp $ 3282cd038dSYoshinobu Inoue */ 3382cd038dSYoshinobu Inoue 34b48287a3SDavid E. O'Brien #include <sys/cdefs.h> 35b48287a3SDavid E. O'Brien __FBSDID("$FreeBSD$"); 36b48287a3SDavid E. O'Brien 37aaa46574SAdrian Chadd #include "opt_rss.h" 38aaa46574SAdrian Chadd 3982cd038dSYoshinobu Inoue #include <sys/param.h> 4082cd038dSYoshinobu Inoue #include <sys/systm.h> 4182cd038dSYoshinobu Inoue #include <sys/malloc.h> 4282cd038dSYoshinobu Inoue #include <sys/mbuf.h> 4382cd038dSYoshinobu Inoue #include <sys/domain.h> 44ea8d1492SAlexander V. Chernikov #include <sys/eventhandler.h> 4582cd038dSYoshinobu Inoue #include <sys/protosw.h> 4682cd038dSYoshinobu Inoue #include <sys/socket.h> 4782cd038dSYoshinobu Inoue #include <sys/errno.h> 4882cd038dSYoshinobu Inoue #include <sys/time.h> 4982cd038dSYoshinobu Inoue #include <sys/kernel.h> 5082cd038dSYoshinobu Inoue #include <sys/syslog.h> 5182cd038dSYoshinobu Inoue 5282cd038dSYoshinobu Inoue #include <net/if.h> 5376039bc8SGleb Smirnoff #include <net/if_var.h> 54aaa46574SAdrian Chadd #include <net/netisr.h> 5582cd038dSYoshinobu Inoue #include <net/route.h> 56eddfbb76SRobert Watson #include <net/vnet.h> 5782cd038dSYoshinobu Inoue 5882cd038dSYoshinobu Inoue #include <netinet/in.h> 5982cd038dSYoshinobu Inoue #include <netinet/in_var.h> 60686cdd19SJun-ichiro itojun Hagino #include <netinet/ip6.h> 6182cd038dSYoshinobu Inoue #include <netinet6/ip6_var.h> 62686cdd19SJun-ichiro itojun Hagino #include <netinet/icmp6.h> 6359dfcba4SHajimu UMEMOTO #include <netinet/in_systm.h> /* for ECN definitions */ 6459dfcba4SHajimu UMEMOTO #include <netinet/ip.h> /* for ECN definitions */ 6582cd038dSYoshinobu Inoue 664b908c8bSRobert Watson #include <security/mac/mac_framework.h> 674b908c8bSRobert Watson 689233d8f3SDavid E. O'Brien static void frag6_enq(struct ip6asfrag *, struct ip6asfrag *); 699233d8f3SDavid E. O'Brien static void frag6_deq(struct ip6asfrag *); 709233d8f3SDavid E. O'Brien static void frag6_insque(struct ip6q *, struct ip6q *); 719233d8f3SDavid E. O'Brien static void frag6_remque(struct ip6q *); 729233d8f3SDavid E. O'Brien static void frag6_freef(struct ip6q *); 7382cd038dSYoshinobu Inoue 7431e8f7e5SHajimu UMEMOTO static struct mtx ip6qlock; 7531e8f7e5SHajimu UMEMOTO /* 7631e8f7e5SHajimu UMEMOTO * These fields all protected by ip6qlock. 7731e8f7e5SHajimu UMEMOTO */ 783e288e62SDimitry Andric static VNET_DEFINE(u_int, frag6_nfragpackets); 793e288e62SDimitry Andric static VNET_DEFINE(u_int, frag6_nfrags); 803e288e62SDimitry Andric static VNET_DEFINE(struct ip6q, ip6q); /* ip6 reassemble queue */ 81eddfbb76SRobert Watson 821e77c105SRobert Watson #define V_frag6_nfragpackets VNET(frag6_nfragpackets) 831e77c105SRobert Watson #define V_frag6_nfrags VNET(frag6_nfrags) 841e77c105SRobert Watson #define V_ip6q VNET(ip6q) 8582cd038dSYoshinobu Inoue 8631e8f7e5SHajimu UMEMOTO #define IP6Q_LOCK_INIT() mtx_init(&ip6qlock, "ip6qlock", NULL, MTX_DEF); 8731e8f7e5SHajimu UMEMOTO #define IP6Q_LOCK() mtx_lock(&ip6qlock) 8831e8f7e5SHajimu UMEMOTO #define IP6Q_TRYLOCK() mtx_trylock(&ip6qlock) 899bcf770cSHajimu UMEMOTO #define IP6Q_LOCK_ASSERT() mtx_assert(&ip6qlock, MA_OWNED) 9031e8f7e5SHajimu UMEMOTO #define IP6Q_UNLOCK() mtx_unlock(&ip6qlock) 919888c401SHajimu UMEMOTO 92959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header"); 93686cdd19SJun-ichiro itojun Hagino 9482cd038dSYoshinobu Inoue /* 9582cd038dSYoshinobu Inoue * Initialise reassembly queue and fragment identifier. 9682cd038dSYoshinobu Inoue */ 974f590175SPaul Saab static void 984f590175SPaul Saab frag6_change(void *tag) 994f590175SPaul Saab { 1004f590175SPaul Saab 101603724d3SBjoern A. Zeeb V_ip6_maxfragpackets = nmbclusters / 4; 102603724d3SBjoern A. Zeeb V_ip6_maxfrags = nmbclusters / 4; 1034f590175SPaul Saab } 1044f590175SPaul Saab 10582cd038dSYoshinobu Inoue void 1061272577eSXin LI frag6_init(void) 10782cd038dSYoshinobu Inoue { 10882cd038dSYoshinobu Inoue 109603724d3SBjoern A. Zeeb V_ip6_maxfragpackets = nmbclusters / 4; 110603724d3SBjoern A. Zeeb V_ip6_maxfrags = nmbclusters / 4; 11182cea7e6SBjoern A. Zeeb V_ip6q.ip6q_next = V_ip6q.ip6q_prev = &V_ip6q; 1121ed81b73SMarko Zec 1131ed81b73SMarko Zec if (!IS_DEFAULT_VNET(curvnet)) 1141ed81b73SMarko Zec return; 11596c2b042SJesper Skriver 1161ed81b73SMarko Zec EVENTHANDLER_REGISTER(nmbclusters_change, 1171ed81b73SMarko Zec frag6_change, NULL, EVENTHANDLER_PRI_ANY); 11882cea7e6SBjoern A. Zeeb 11982cea7e6SBjoern A. Zeeb IP6Q_LOCK_INIT(); 12082cd038dSYoshinobu Inoue } 12182cd038dSYoshinobu Inoue 12282cd038dSYoshinobu Inoue /* 123686cdd19SJun-ichiro itojun Hagino * In RFC2460, fragment and reassembly rule do not agree with each other, 124686cdd19SJun-ichiro itojun Hagino * in terms of next header field handling in fragment header. 125686cdd19SJun-ichiro itojun Hagino * While the sender will use the same value for all of the fragmented packets, 126686cdd19SJun-ichiro itojun Hagino * receiver is suggested not to check the consistency. 127686cdd19SJun-ichiro itojun Hagino * 128686cdd19SJun-ichiro itojun Hagino * fragment rule (p20): 129686cdd19SJun-ichiro itojun Hagino * (2) A Fragment header containing: 130686cdd19SJun-ichiro itojun Hagino * The Next Header value that identifies the first header of 131686cdd19SJun-ichiro itojun Hagino * the Fragmentable Part of the original packet. 132686cdd19SJun-ichiro itojun Hagino * -> next header field is same for all fragments 133686cdd19SJun-ichiro itojun Hagino * 134686cdd19SJun-ichiro itojun Hagino * reassembly rule (p21): 135686cdd19SJun-ichiro itojun Hagino * The Next Header field of the last header of the Unfragmentable 136686cdd19SJun-ichiro itojun Hagino * Part is obtained from the Next Header field of the first 137686cdd19SJun-ichiro itojun Hagino * fragment's Fragment header. 138686cdd19SJun-ichiro itojun Hagino * -> should grab it from the first fragment only 139686cdd19SJun-ichiro itojun Hagino * 140686cdd19SJun-ichiro itojun Hagino * The following note also contradicts with fragment rule - no one is going to 141686cdd19SJun-ichiro itojun Hagino * send different fragment with different next header field. 142686cdd19SJun-ichiro itojun Hagino * 143686cdd19SJun-ichiro itojun Hagino * additional note (p22): 144686cdd19SJun-ichiro itojun Hagino * The Next Header values in the Fragment headers of different 145686cdd19SJun-ichiro itojun Hagino * fragments of the same original packet may differ. Only the value 146686cdd19SJun-ichiro itojun Hagino * from the Offset zero fragment packet is used for reassembly. 147686cdd19SJun-ichiro itojun Hagino * -> should grab it from the first fragment only 148686cdd19SJun-ichiro itojun Hagino * 149686cdd19SJun-ichiro itojun Hagino * There is no explicit reason given in the RFC. Historical reason maybe? 150686cdd19SJun-ichiro itojun Hagino */ 151686cdd19SJun-ichiro itojun Hagino /* 15282cd038dSYoshinobu Inoue * Fragment input 15382cd038dSYoshinobu Inoue */ 15482cd038dSYoshinobu Inoue int 1551272577eSXin LI frag6_input(struct mbuf **mp, int *offp, int proto) 15682cd038dSYoshinobu Inoue { 15782cd038dSYoshinobu Inoue struct mbuf *m = *mp, *t; 15882cd038dSYoshinobu Inoue struct ip6_hdr *ip6; 15982cd038dSYoshinobu Inoue struct ip6_frag *ip6f; 16082cd038dSYoshinobu Inoue struct ip6q *q6; 161686cdd19SJun-ichiro itojun Hagino struct ip6asfrag *af6, *ip6af, *af6dwn; 1622a5aafceSHajimu UMEMOTO struct in6_ifaddr *ia; 16382cd038dSYoshinobu Inoue int offset = *offp, nxt, i, next; 16482cd038dSYoshinobu Inoue int first_frag = 0; 165686cdd19SJun-ichiro itojun Hagino int fragoff, frgpartlen; /* must be larger than u_int16_t */ 16682cd038dSYoshinobu Inoue struct ifnet *dstifp; 16759dfcba4SHajimu UMEMOTO u_int8_t ecn, ecn0; 168aaa46574SAdrian Chadd #ifdef RSS 169aaa46574SAdrian Chadd struct m_tag *mtag; 170aaa46574SAdrian Chadd struct ip6_direct_ctx *ip6dc; 171aaa46574SAdrian Chadd #endif 172aaa46574SAdrian Chadd 1731d54aa3bSBjoern A. Zeeb #if 0 1741d54aa3bSBjoern A. Zeeb char ip6buf[INET6_ADDRSTRLEN]; 1751d54aa3bSBjoern A. Zeeb #endif 17682cd038dSYoshinobu Inoue 17782cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 178686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 179686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); 18082cd038dSYoshinobu Inoue ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); 181686cdd19SJun-ichiro itojun Hagino #else 182686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); 183686cdd19SJun-ichiro itojun Hagino if (ip6f == NULL) 18440e39bbbSHajimu UMEMOTO return (IPPROTO_DONE); 185686cdd19SJun-ichiro itojun Hagino #endif 18682cd038dSYoshinobu Inoue 18782cd038dSYoshinobu Inoue dstifp = NULL; 18882cd038dSYoshinobu Inoue /* find the destination interface of the packet. */ 1893e88eb90SAndrey V. Elsukov ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); 1903e88eb90SAndrey V. Elsukov if (ia != NULL) { 1912a5aafceSHajimu UMEMOTO dstifp = ia->ia_ifp; 1928c0fec80SRobert Watson ifa_free(&ia->ia_ifa); 1938c0fec80SRobert Watson } 19482cd038dSYoshinobu Inoue /* jumbo payload can't contain a fragment header */ 19582cd038dSYoshinobu Inoue if (ip6->ip6_plen == 0) { 19682cd038dSYoshinobu Inoue icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); 19782cd038dSYoshinobu Inoue in6_ifstat_inc(dstifp, ifs6_reass_fail); 19882cd038dSYoshinobu Inoue return IPPROTO_DONE; 19982cd038dSYoshinobu Inoue } 20082cd038dSYoshinobu Inoue 20182cd038dSYoshinobu Inoue /* 20282cd038dSYoshinobu Inoue * check whether fragment packet's fragment length is 20382cd038dSYoshinobu Inoue * multiple of 8 octets. 20482cd038dSYoshinobu Inoue * sizeof(struct ip6_frag) == 8 20582cd038dSYoshinobu Inoue * sizeof(struct ip6_hdr) = 40 20682cd038dSYoshinobu Inoue */ 20782cd038dSYoshinobu Inoue if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && 20882cd038dSYoshinobu Inoue (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { 20906cd0a3fSHajimu UMEMOTO icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 210686cdd19SJun-ichiro itojun Hagino offsetof(struct ip6_hdr, ip6_plen)); 21182cd038dSYoshinobu Inoue in6_ifstat_inc(dstifp, ifs6_reass_fail); 21282cd038dSYoshinobu Inoue return IPPROTO_DONE; 21382cd038dSYoshinobu Inoue } 21482cd038dSYoshinobu Inoue 2159cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_fragments); 21682cd038dSYoshinobu Inoue in6_ifstat_inc(dstifp, ifs6_reass_reqd); 21782cd038dSYoshinobu Inoue 218686cdd19SJun-ichiro itojun Hagino /* offset now points to data portion */ 21982cd038dSYoshinobu Inoue offset += sizeof(struct ip6_frag); 22082cd038dSYoshinobu Inoue 2214018ea9aSBjoern A. Zeeb /* 222fd291ae3SBjoern A. Zeeb * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0) 223fd291ae3SBjoern A. Zeeb * upfront, unrelated to any reassembly. Just skip the fragment header. 2244018ea9aSBjoern A. Zeeb */ 2254018ea9aSBjoern A. Zeeb if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) { 2264018ea9aSBjoern A. Zeeb /* XXX-BZ we want dedicated counters for this. */ 2279cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_reassembled); 2284018ea9aSBjoern A. Zeeb in6_ifstat_inc(dstifp, ifs6_reass_ok); 2294018ea9aSBjoern A. Zeeb *offp = offset; 2304018ea9aSBjoern A. Zeeb return (ip6f->ip6f_nxt); 2314018ea9aSBjoern A. Zeeb } 2324018ea9aSBjoern A. Zeeb 2339888c401SHajimu UMEMOTO IP6Q_LOCK(); 2349888c401SHajimu UMEMOTO 2359888c401SHajimu UMEMOTO /* 2369888c401SHajimu UMEMOTO * Enforce upper bound on number of fragments. 2379888c401SHajimu UMEMOTO * If maxfrag is 0, never accept fragments. 2389888c401SHajimu UMEMOTO * If maxfrag is -1, accept all fragments without limitation. 2399888c401SHajimu UMEMOTO */ 240603724d3SBjoern A. Zeeb if (V_ip6_maxfrags < 0) 2419888c401SHajimu UMEMOTO ; 242603724d3SBjoern A. Zeeb else if (V_frag6_nfrags >= (u_int)V_ip6_maxfrags) 2439888c401SHajimu UMEMOTO goto dropfrag; 24433841545SHajimu UMEMOTO 245603724d3SBjoern A. Zeeb for (q6 = V_ip6q.ip6q_next; q6 != &V_ip6q; q6 = q6->ip6q_next) 24682cd038dSYoshinobu Inoue if (ip6f->ip6f_ident == q6->ip6q_ident && 24782cd038dSYoshinobu Inoue IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && 2484b908c8bSRobert Watson IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst) 2494b908c8bSRobert Watson #ifdef MAC 2504b908c8bSRobert Watson && mac_ip6q_match(m, q6) 2514b908c8bSRobert Watson #endif 2524b908c8bSRobert Watson ) 25382cd038dSYoshinobu Inoue break; 25482cd038dSYoshinobu Inoue 255603724d3SBjoern A. Zeeb if (q6 == &V_ip6q) { 25682cd038dSYoshinobu Inoue /* 25782cd038dSYoshinobu Inoue * the first fragment to arrive, create a reassembly queue. 25882cd038dSYoshinobu Inoue */ 25982cd038dSYoshinobu Inoue first_frag = 1; 26082cd038dSYoshinobu Inoue 26182cd038dSYoshinobu Inoue /* 26282cd038dSYoshinobu Inoue * Enforce upper bound on number of fragmented packets 26382cd038dSYoshinobu Inoue * for which we attempt reassembly; 2649888c401SHajimu UMEMOTO * If maxfragpackets is 0, never accept fragments. 2659888c401SHajimu UMEMOTO * If maxfragpackets is -1, accept all fragments without 2669888c401SHajimu UMEMOTO * limitation. 26782cd038dSYoshinobu Inoue */ 268603724d3SBjoern A. Zeeb if (V_ip6_maxfragpackets < 0) 26933841545SHajimu UMEMOTO ; 270603724d3SBjoern A. Zeeb else if (V_frag6_nfragpackets >= (u_int)V_ip6_maxfragpackets) 27133841545SHajimu UMEMOTO goto dropfrag; 272603724d3SBjoern A. Zeeb V_frag6_nfragpackets++; 27382cd038dSYoshinobu Inoue q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, 2748c0dd0e4SHajimu UMEMOTO M_NOWAIT); 27582cd038dSYoshinobu Inoue if (q6 == NULL) 27682cd038dSYoshinobu Inoue goto dropfrag; 277686cdd19SJun-ichiro itojun Hagino bzero(q6, sizeof(*q6)); 2784b908c8bSRobert Watson #ifdef MAC 2794b908c8bSRobert Watson if (mac_ip6q_init(q6, M_NOWAIT) != 0) { 2804b908c8bSRobert Watson free(q6, M_FTABLE); 2814b908c8bSRobert Watson goto dropfrag; 2824b908c8bSRobert Watson } 2834b908c8bSRobert Watson mac_ip6q_create(m, q6); 2844b908c8bSRobert Watson #endif 285603724d3SBjoern A. Zeeb frag6_insque(q6, &V_ip6q); 28682cd038dSYoshinobu Inoue 287686cdd19SJun-ichiro itojun Hagino /* ip6q_nxt will be filled afterwards, from 1st fragment */ 28882cd038dSYoshinobu Inoue q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; 289686cdd19SJun-ichiro itojun Hagino #ifdef notyet 290686cdd19SJun-ichiro itojun Hagino q6->ip6q_nxtp = (u_char *)nxtp; 291686cdd19SJun-ichiro itojun Hagino #endif 29282cd038dSYoshinobu Inoue q6->ip6q_ident = ip6f->ip6f_ident; 29382cd038dSYoshinobu Inoue q6->ip6q_ttl = IPV6_FRAGTTL; 29482cd038dSYoshinobu Inoue q6->ip6q_src = ip6->ip6_src; 29582cd038dSYoshinobu Inoue q6->ip6q_dst = ip6->ip6_dst; 2965e9510e3SJINMEI Tatuya q6->ip6q_ecn = 2975e9510e3SJINMEI Tatuya (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; 29882cd038dSYoshinobu Inoue q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ 2999888c401SHajimu UMEMOTO 3009888c401SHajimu UMEMOTO q6->ip6q_nfrag = 0; 30182cd038dSYoshinobu Inoue } 30282cd038dSYoshinobu Inoue 30382cd038dSYoshinobu Inoue /* 30482cd038dSYoshinobu Inoue * If it's the 1st fragment, record the length of the 30582cd038dSYoshinobu Inoue * unfragmentable part and the next header of the fragment header. 30682cd038dSYoshinobu Inoue */ 30782cd038dSYoshinobu Inoue fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); 30882cd038dSYoshinobu Inoue if (fragoff == 0) { 30906cd0a3fSHajimu UMEMOTO q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) - 31006cd0a3fSHajimu UMEMOTO sizeof(struct ip6_frag); 31182cd038dSYoshinobu Inoue q6->ip6q_nxt = ip6f->ip6f_nxt; 31282cd038dSYoshinobu Inoue } 31382cd038dSYoshinobu Inoue 31482cd038dSYoshinobu Inoue /* 31582cd038dSYoshinobu Inoue * Check that the reassembled packet would not exceed 65535 bytes 31682cd038dSYoshinobu Inoue * in size. 31782cd038dSYoshinobu Inoue * If it would exceed, discard the fragment and return an ICMP error. 31882cd038dSYoshinobu Inoue */ 31982cd038dSYoshinobu Inoue frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; 32082cd038dSYoshinobu Inoue if (q6->ip6q_unfrglen >= 0) { 32182cd038dSYoshinobu Inoue /* The 1st fragment has already arrived. */ 32282cd038dSYoshinobu Inoue if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) { 32382cd038dSYoshinobu Inoue icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 324686cdd19SJun-ichiro itojun Hagino offset - sizeof(struct ip6_frag) + 325686cdd19SJun-ichiro itojun Hagino offsetof(struct ip6_frag, ip6f_offlg)); 3269888c401SHajimu UMEMOTO IP6Q_UNLOCK(); 32782cd038dSYoshinobu Inoue return (IPPROTO_DONE); 32882cd038dSYoshinobu Inoue } 32906cd0a3fSHajimu UMEMOTO } else if (fragoff + frgpartlen > IPV6_MAXPACKET) { 33082cd038dSYoshinobu Inoue icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 331686cdd19SJun-ichiro itojun Hagino offset - sizeof(struct ip6_frag) + 332686cdd19SJun-ichiro itojun Hagino offsetof(struct ip6_frag, ip6f_offlg)); 3339888c401SHajimu UMEMOTO IP6Q_UNLOCK(); 33482cd038dSYoshinobu Inoue return (IPPROTO_DONE); 33582cd038dSYoshinobu Inoue } 33682cd038dSYoshinobu Inoue /* 33782cd038dSYoshinobu Inoue * If it's the first fragment, do the above check for each 33882cd038dSYoshinobu Inoue * fragment already stored in the reassembly queue. 33982cd038dSYoshinobu Inoue */ 34082cd038dSYoshinobu Inoue if (fragoff == 0) { 34182cd038dSYoshinobu Inoue for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; 34282cd038dSYoshinobu Inoue af6 = af6dwn) { 34382cd038dSYoshinobu Inoue af6dwn = af6->ip6af_down; 34482cd038dSYoshinobu Inoue 34582cd038dSYoshinobu Inoue if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen > 34682cd038dSYoshinobu Inoue IPV6_MAXPACKET) { 34782cd038dSYoshinobu Inoue struct mbuf *merr = IP6_REASS_MBUF(af6); 34882cd038dSYoshinobu Inoue struct ip6_hdr *ip6err; 34982cd038dSYoshinobu Inoue int erroff = af6->ip6af_offset; 35082cd038dSYoshinobu Inoue 35182cd038dSYoshinobu Inoue /* dequeue the fragment. */ 35282cd038dSYoshinobu Inoue frag6_deq(af6); 353686cdd19SJun-ichiro itojun Hagino free(af6, M_FTABLE); 35482cd038dSYoshinobu Inoue 35582cd038dSYoshinobu Inoue /* adjust pointer. */ 35682cd038dSYoshinobu Inoue ip6err = mtod(merr, struct ip6_hdr *); 35782cd038dSYoshinobu Inoue 35882cd038dSYoshinobu Inoue /* 35982cd038dSYoshinobu Inoue * Restore source and destination addresses 36082cd038dSYoshinobu Inoue * in the erroneous IPv6 header. 36182cd038dSYoshinobu Inoue */ 36282cd038dSYoshinobu Inoue ip6err->ip6_src = q6->ip6q_src; 36382cd038dSYoshinobu Inoue ip6err->ip6_dst = q6->ip6q_dst; 36482cd038dSYoshinobu Inoue 36582cd038dSYoshinobu Inoue icmp6_error(merr, ICMP6_PARAM_PROB, 36682cd038dSYoshinobu Inoue ICMP6_PARAMPROB_HEADER, 367686cdd19SJun-ichiro itojun Hagino erroff - sizeof(struct ip6_frag) + 368686cdd19SJun-ichiro itojun Hagino offsetof(struct ip6_frag, ip6f_offlg)); 36982cd038dSYoshinobu Inoue } 37082cd038dSYoshinobu Inoue } 37182cd038dSYoshinobu Inoue } 37282cd038dSYoshinobu Inoue 373686cdd19SJun-ichiro itojun Hagino ip6af = (struct ip6asfrag *)malloc(sizeof(struct ip6asfrag), M_FTABLE, 3748c0dd0e4SHajimu UMEMOTO M_NOWAIT); 375686cdd19SJun-ichiro itojun Hagino if (ip6af == NULL) 376686cdd19SJun-ichiro itojun Hagino goto dropfrag; 377686cdd19SJun-ichiro itojun Hagino bzero(ip6af, sizeof(*ip6af)); 37882cd038dSYoshinobu Inoue ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; 37982cd038dSYoshinobu Inoue ip6af->ip6af_off = fragoff; 38082cd038dSYoshinobu Inoue ip6af->ip6af_frglen = frgpartlen; 38182cd038dSYoshinobu Inoue ip6af->ip6af_offset = offset; 38282cd038dSYoshinobu Inoue IP6_REASS_MBUF(ip6af) = m; 38382cd038dSYoshinobu Inoue 38482cd038dSYoshinobu Inoue if (first_frag) { 38582cd038dSYoshinobu Inoue af6 = (struct ip6asfrag *)q6; 38682cd038dSYoshinobu Inoue goto insert; 38782cd038dSYoshinobu Inoue } 38882cd038dSYoshinobu Inoue 38982cd038dSYoshinobu Inoue /* 39059dfcba4SHajimu UMEMOTO * Handle ECN by comparing this segment with the first one; 39159dfcba4SHajimu UMEMOTO * if CE is set, do not lose CE. 39259dfcba4SHajimu UMEMOTO * drop if CE and not-ECT are mixed for the same packet. 39359dfcba4SHajimu UMEMOTO */ 39459dfcba4SHajimu UMEMOTO ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; 3955e9510e3SJINMEI Tatuya ecn0 = q6->ip6q_ecn; 39659dfcba4SHajimu UMEMOTO if (ecn == IPTOS_ECN_CE) { 39759dfcba4SHajimu UMEMOTO if (ecn0 == IPTOS_ECN_NOTECT) { 39859dfcba4SHajimu UMEMOTO free(ip6af, M_FTABLE); 39959dfcba4SHajimu UMEMOTO goto dropfrag; 40059dfcba4SHajimu UMEMOTO } 40159dfcba4SHajimu UMEMOTO if (ecn0 != IPTOS_ECN_CE) 4025e9510e3SJINMEI Tatuya q6->ip6q_ecn = IPTOS_ECN_CE; 40359dfcba4SHajimu UMEMOTO } 40459dfcba4SHajimu UMEMOTO if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT) { 40559dfcba4SHajimu UMEMOTO free(ip6af, M_FTABLE); 40659dfcba4SHajimu UMEMOTO goto dropfrag; 40759dfcba4SHajimu UMEMOTO } 40859dfcba4SHajimu UMEMOTO 40959dfcba4SHajimu UMEMOTO /* 41082cd038dSYoshinobu Inoue * Find a segment which begins after this one does. 41182cd038dSYoshinobu Inoue */ 41282cd038dSYoshinobu Inoue for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; 41382cd038dSYoshinobu Inoue af6 = af6->ip6af_down) 41482cd038dSYoshinobu Inoue if (af6->ip6af_off > ip6af->ip6af_off) 41582cd038dSYoshinobu Inoue break; 41682cd038dSYoshinobu Inoue 417686cdd19SJun-ichiro itojun Hagino #if 0 418686cdd19SJun-ichiro itojun Hagino /* 419686cdd19SJun-ichiro itojun Hagino * If there is a preceding segment, it may provide some of 420686cdd19SJun-ichiro itojun Hagino * our data already. If so, drop the data from the incoming 421686cdd19SJun-ichiro itojun Hagino * segment. If it provides all of our data, drop us. 422686cdd19SJun-ichiro itojun Hagino */ 423686cdd19SJun-ichiro itojun Hagino if (af6->ip6af_up != (struct ip6asfrag *)q6) { 424686cdd19SJun-ichiro itojun Hagino i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen 425686cdd19SJun-ichiro itojun Hagino - ip6af->ip6af_off; 426686cdd19SJun-ichiro itojun Hagino if (i > 0) { 427686cdd19SJun-ichiro itojun Hagino if (i >= ip6af->ip6af_frglen) 428686cdd19SJun-ichiro itojun Hagino goto dropfrag; 429686cdd19SJun-ichiro itojun Hagino m_adj(IP6_REASS_MBUF(ip6af), i); 430686cdd19SJun-ichiro itojun Hagino ip6af->ip6af_off += i; 431686cdd19SJun-ichiro itojun Hagino ip6af->ip6af_frglen -= i; 432686cdd19SJun-ichiro itojun Hagino } 433686cdd19SJun-ichiro itojun Hagino } 434686cdd19SJun-ichiro itojun Hagino 435686cdd19SJun-ichiro itojun Hagino /* 436686cdd19SJun-ichiro itojun Hagino * While we overlap succeeding segments trim them or, 437686cdd19SJun-ichiro itojun Hagino * if they are completely covered, dequeue them. 438686cdd19SJun-ichiro itojun Hagino */ 439686cdd19SJun-ichiro itojun Hagino while (af6 != (struct ip6asfrag *)q6 && 440686cdd19SJun-ichiro itojun Hagino ip6af->ip6af_off + ip6af->ip6af_frglen > af6->ip6af_off) { 441686cdd19SJun-ichiro itojun Hagino i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; 442686cdd19SJun-ichiro itojun Hagino if (i < af6->ip6af_frglen) { 443686cdd19SJun-ichiro itojun Hagino af6->ip6af_frglen -= i; 444686cdd19SJun-ichiro itojun Hagino af6->ip6af_off += i; 445686cdd19SJun-ichiro itojun Hagino m_adj(IP6_REASS_MBUF(af6), i); 446686cdd19SJun-ichiro itojun Hagino break; 447686cdd19SJun-ichiro itojun Hagino } 448686cdd19SJun-ichiro itojun Hagino af6 = af6->ip6af_down; 449686cdd19SJun-ichiro itojun Hagino m_freem(IP6_REASS_MBUF(af6->ip6af_up)); 450686cdd19SJun-ichiro itojun Hagino frag6_deq(af6->ip6af_up); 451686cdd19SJun-ichiro itojun Hagino } 452686cdd19SJun-ichiro itojun Hagino #else 45382cd038dSYoshinobu Inoue /* 45482cd038dSYoshinobu Inoue * If the incoming framgent overlaps some existing fragments in 45582cd038dSYoshinobu Inoue * the reassembly queue, drop it, since it is dangerous to override 45682cd038dSYoshinobu Inoue * existing fragments from a security point of view. 4579888c401SHajimu UMEMOTO * We don't know which fragment is the bad guy - here we trust 4589888c401SHajimu UMEMOTO * fragment that came in earlier, with no real reason. 4595e9510e3SJINMEI Tatuya * 4605e9510e3SJINMEI Tatuya * Note: due to changes after disabling this part, mbuf passed to 4615e9510e3SJINMEI Tatuya * m_adj() below now does not meet the requirement. 46282cd038dSYoshinobu Inoue */ 46382cd038dSYoshinobu Inoue if (af6->ip6af_up != (struct ip6asfrag *)q6) { 46482cd038dSYoshinobu Inoue i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen 46582cd038dSYoshinobu Inoue - ip6af->ip6af_off; 46682cd038dSYoshinobu Inoue if (i > 0) { 4672bf76779SMunechika SUMIKAWA #if 0 /* suppress the noisy log */ 46882cd038dSYoshinobu Inoue log(LOG_ERR, "%d bytes of a fragment from %s " 46982cd038dSYoshinobu Inoue "overlaps the previous fragment\n", 4701d54aa3bSBjoern A. Zeeb i, ip6_sprintf(ip6buf, &q6->ip6q_src)); 4712bf76779SMunechika SUMIKAWA #endif 4722bf76779SMunechika SUMIKAWA free(ip6af, M_FTABLE); 47382cd038dSYoshinobu Inoue goto dropfrag; 47482cd038dSYoshinobu Inoue } 47582cd038dSYoshinobu Inoue } 47682cd038dSYoshinobu Inoue if (af6 != (struct ip6asfrag *)q6) { 47782cd038dSYoshinobu Inoue i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; 47882cd038dSYoshinobu Inoue if (i > 0) { 4792bf76779SMunechika SUMIKAWA #if 0 /* suppress the noisy log */ 48082cd038dSYoshinobu Inoue log(LOG_ERR, "%d bytes of a fragment from %s " 48182cd038dSYoshinobu Inoue "overlaps the succeeding fragment", 4821d54aa3bSBjoern A. Zeeb i, ip6_sprintf(ip6buf, &q6->ip6q_src)); 4832bf76779SMunechika SUMIKAWA #endif 4842bf76779SMunechika SUMIKAWA free(ip6af, M_FTABLE); 48582cd038dSYoshinobu Inoue goto dropfrag; 48682cd038dSYoshinobu Inoue } 48782cd038dSYoshinobu Inoue } 488686cdd19SJun-ichiro itojun Hagino #endif 48982cd038dSYoshinobu Inoue 49082cd038dSYoshinobu Inoue insert: 4914b908c8bSRobert Watson #ifdef MAC 4924b908c8bSRobert Watson if (!first_frag) 4934b908c8bSRobert Watson mac_ip6q_update(m, q6); 4944b908c8bSRobert Watson #endif 49582cd038dSYoshinobu Inoue 49682cd038dSYoshinobu Inoue /* 49782cd038dSYoshinobu Inoue * Stick new segment in its place; 49882cd038dSYoshinobu Inoue * check for complete reassembly. 49982cd038dSYoshinobu Inoue * Move to front of packet queue, as we are 50082cd038dSYoshinobu Inoue * the most recently active fragmented packet. 50182cd038dSYoshinobu Inoue */ 50282cd038dSYoshinobu Inoue frag6_enq(ip6af, af6->ip6af_up); 503603724d3SBjoern A. Zeeb V_frag6_nfrags++; 5049888c401SHajimu UMEMOTO q6->ip6q_nfrag++; 505686cdd19SJun-ichiro itojun Hagino #if 0 /* xxx */ 506603724d3SBjoern A. Zeeb if (q6 != V_ip6q.ip6q_next) { 507686cdd19SJun-ichiro itojun Hagino frag6_remque(q6); 508603724d3SBjoern A. Zeeb frag6_insque(q6, &V_ip6q); 509686cdd19SJun-ichiro itojun Hagino } 510686cdd19SJun-ichiro itojun Hagino #endif 51182cd038dSYoshinobu Inoue next = 0; 51282cd038dSYoshinobu Inoue for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; 51382cd038dSYoshinobu Inoue af6 = af6->ip6af_down) { 51482cd038dSYoshinobu Inoue if (af6->ip6af_off != next) { 5159888c401SHajimu UMEMOTO IP6Q_UNLOCK(); 51682cd038dSYoshinobu Inoue return IPPROTO_DONE; 51782cd038dSYoshinobu Inoue } 51882cd038dSYoshinobu Inoue next += af6->ip6af_frglen; 51982cd038dSYoshinobu Inoue } 52082cd038dSYoshinobu Inoue if (af6->ip6af_up->ip6af_mff) { 5219888c401SHajimu UMEMOTO IP6Q_UNLOCK(); 52282cd038dSYoshinobu Inoue return IPPROTO_DONE; 52382cd038dSYoshinobu Inoue } 52482cd038dSYoshinobu Inoue 52582cd038dSYoshinobu Inoue /* 52682cd038dSYoshinobu Inoue * Reassembly is complete; concatenate fragments. 52782cd038dSYoshinobu Inoue */ 52882cd038dSYoshinobu Inoue ip6af = q6->ip6q_down; 52982cd038dSYoshinobu Inoue t = m = IP6_REASS_MBUF(ip6af); 53082cd038dSYoshinobu Inoue af6 = ip6af->ip6af_down; 531686cdd19SJun-ichiro itojun Hagino frag6_deq(ip6af); 53282cd038dSYoshinobu Inoue while (af6 != (struct ip6asfrag *)q6) { 5339907aba3SAndrey V. Elsukov m->m_pkthdr.csum_flags &= 5349907aba3SAndrey V. Elsukov IP6_REASS_MBUF(af6)->m_pkthdr.csum_flags; 5359907aba3SAndrey V. Elsukov m->m_pkthdr.csum_data += 5369907aba3SAndrey V. Elsukov IP6_REASS_MBUF(af6)->m_pkthdr.csum_data; 5379907aba3SAndrey V. Elsukov 538686cdd19SJun-ichiro itojun Hagino af6dwn = af6->ip6af_down; 539686cdd19SJun-ichiro itojun Hagino frag6_deq(af6); 54082cd038dSYoshinobu Inoue while (t->m_next) 54182cd038dSYoshinobu Inoue t = t->m_next; 542ba99cc0bSAlexander V. Chernikov m_adj(IP6_REASS_MBUF(af6), af6->ip6af_offset); 543ba99cc0bSAlexander V. Chernikov m_cat(t, IP6_REASS_MBUF(af6)); 544686cdd19SJun-ichiro itojun Hagino free(af6, M_FTABLE); 545686cdd19SJun-ichiro itojun Hagino af6 = af6dwn; 54682cd038dSYoshinobu Inoue } 54782cd038dSYoshinobu Inoue 5489907aba3SAndrey V. Elsukov while (m->m_pkthdr.csum_data & 0xffff0000) 5499907aba3SAndrey V. Elsukov m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data & 0xffff) + 5509907aba3SAndrey V. Elsukov (m->m_pkthdr.csum_data >> 16); 5519907aba3SAndrey V. Elsukov 55282cd038dSYoshinobu Inoue /* adjust offset to point where the original next header starts */ 55382cd038dSYoshinobu Inoue offset = ip6af->ip6af_offset - sizeof(struct ip6_frag); 554686cdd19SJun-ichiro itojun Hagino free(ip6af, M_FTABLE); 555686cdd19SJun-ichiro itojun Hagino ip6 = mtod(m, struct ip6_hdr *); 55682cd038dSYoshinobu Inoue ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr)); 5575e9510e3SJINMEI Tatuya if (q6->ip6q_ecn == IPTOS_ECN_CE) 5585e9510e3SJINMEI Tatuya ip6->ip6_flow |= htonl(IPTOS_ECN_CE << 20); 55982cd038dSYoshinobu Inoue nxt = q6->ip6q_nxt; 560686cdd19SJun-ichiro itojun Hagino #ifdef notyet 561686cdd19SJun-ichiro itojun Hagino *q6->ip6q_nxtp = (u_char)(nxt & 0xff); 562686cdd19SJun-ichiro itojun Hagino #endif 56382cd038dSYoshinobu Inoue 5640b438b0fSGleb Smirnoff if (ip6_deletefraghdr(m, offset, M_NOWAIT) != 0) { 565686cdd19SJun-ichiro itojun Hagino frag6_remque(q6); 566603724d3SBjoern A. Zeeb V_frag6_nfrags -= q6->ip6q_nfrag; 5674b908c8bSRobert Watson #ifdef MAC 5684b908c8bSRobert Watson mac_ip6q_destroy(q6); 5694b908c8bSRobert Watson #endif 570686cdd19SJun-ichiro itojun Hagino free(q6, M_FTABLE); 571603724d3SBjoern A. Zeeb V_frag6_nfragpackets--; 5720b438b0fSGleb Smirnoff 573686cdd19SJun-ichiro itojun Hagino goto dropfrag; 57482cd038dSYoshinobu Inoue } 57582cd038dSYoshinobu Inoue 57682cd038dSYoshinobu Inoue /* 57782cd038dSYoshinobu Inoue * Store NXT to the original. 57882cd038dSYoshinobu Inoue */ 57982cd038dSYoshinobu Inoue { 58082cd038dSYoshinobu Inoue char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */ 58182cd038dSYoshinobu Inoue *prvnxtp = nxt; 58282cd038dSYoshinobu Inoue } 58382cd038dSYoshinobu Inoue 58482cd038dSYoshinobu Inoue frag6_remque(q6); 585603724d3SBjoern A. Zeeb V_frag6_nfrags -= q6->ip6q_nfrag; 5864b908c8bSRobert Watson #ifdef MAC 5874b908c8bSRobert Watson mac_ip6q_reassemble(q6, m); 5884b908c8bSRobert Watson mac_ip6q_destroy(q6); 5894b908c8bSRobert Watson #endif 59082cd038dSYoshinobu Inoue free(q6, M_FTABLE); 591603724d3SBjoern A. Zeeb V_frag6_nfragpackets--; 59282cd038dSYoshinobu Inoue 59382cd038dSYoshinobu Inoue if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */ 59482cd038dSYoshinobu Inoue int plen = 0; 59582cd038dSYoshinobu Inoue for (t = m; t; t = t->m_next) 59682cd038dSYoshinobu Inoue plen += t->m_len; 59782cd038dSYoshinobu Inoue m->m_pkthdr.len = plen; 59882cd038dSYoshinobu Inoue } 59982cd038dSYoshinobu Inoue 600aaa46574SAdrian Chadd #ifdef RSS 601aaa46574SAdrian Chadd mtag = m_tag_alloc(MTAG_ABI_IPV6, IPV6_TAG_DIRECT, sizeof(*ip6dc), 602aaa46574SAdrian Chadd M_NOWAIT); 603aaa46574SAdrian Chadd if (mtag == NULL) 604aaa46574SAdrian Chadd goto dropfrag; 605aaa46574SAdrian Chadd 606aaa46574SAdrian Chadd ip6dc = (struct ip6_direct_ctx *)(mtag + 1); 607aaa46574SAdrian Chadd ip6dc->ip6dc_nxt = nxt; 608aaa46574SAdrian Chadd ip6dc->ip6dc_off = offset; 609aaa46574SAdrian Chadd 610aaa46574SAdrian Chadd m_tag_prepend(m, mtag); 611aaa46574SAdrian Chadd #endif 612aaa46574SAdrian Chadd 613aaa46574SAdrian Chadd IP6Q_UNLOCK(); 6149cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_reassembled); 61582cd038dSYoshinobu Inoue in6_ifstat_inc(dstifp, ifs6_reass_ok); 61682cd038dSYoshinobu Inoue 617aaa46574SAdrian Chadd #ifdef RSS 618aaa46574SAdrian Chadd /* 619aaa46574SAdrian Chadd * Queue/dispatch for reprocessing. 620aaa46574SAdrian Chadd */ 621aaa46574SAdrian Chadd netisr_dispatch(NETISR_IPV6_DIRECT, m); 622aaa46574SAdrian Chadd return IPPROTO_DONE; 623aaa46574SAdrian Chadd #endif 624aaa46574SAdrian Chadd 62582cd038dSYoshinobu Inoue /* 62682cd038dSYoshinobu Inoue * Tell launch routine the next header 62782cd038dSYoshinobu Inoue */ 62882cd038dSYoshinobu Inoue 62982cd038dSYoshinobu Inoue *mp = m; 63082cd038dSYoshinobu Inoue *offp = offset; 63182cd038dSYoshinobu Inoue 63282cd038dSYoshinobu Inoue return nxt; 63382cd038dSYoshinobu Inoue 63482cd038dSYoshinobu Inoue dropfrag: 63531e8f7e5SHajimu UMEMOTO IP6Q_UNLOCK(); 63682cd038dSYoshinobu Inoue in6_ifstat_inc(dstifp, ifs6_reass_fail); 6379cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_fragdropped); 63882cd038dSYoshinobu Inoue m_freem(m); 63982cd038dSYoshinobu Inoue return IPPROTO_DONE; 64082cd038dSYoshinobu Inoue } 64182cd038dSYoshinobu Inoue 64282cd038dSYoshinobu Inoue /* 64382cd038dSYoshinobu Inoue * Free a fragment reassembly header and all 64482cd038dSYoshinobu Inoue * associated datagrams. 64582cd038dSYoshinobu Inoue */ 64682cd038dSYoshinobu Inoue void 6471272577eSXin LI frag6_freef(struct ip6q *q6) 64882cd038dSYoshinobu Inoue { 64982cd038dSYoshinobu Inoue struct ip6asfrag *af6, *down6; 65082cd038dSYoshinobu Inoue 6519bcf770cSHajimu UMEMOTO IP6Q_LOCK_ASSERT(); 6529888c401SHajimu UMEMOTO 65382cd038dSYoshinobu Inoue for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; 65482cd038dSYoshinobu Inoue af6 = down6) { 65582cd038dSYoshinobu Inoue struct mbuf *m = IP6_REASS_MBUF(af6); 65682cd038dSYoshinobu Inoue 65782cd038dSYoshinobu Inoue down6 = af6->ip6af_down; 65882cd038dSYoshinobu Inoue frag6_deq(af6); 65982cd038dSYoshinobu Inoue 66082cd038dSYoshinobu Inoue /* 66182cd038dSYoshinobu Inoue * Return ICMP time exceeded error for the 1st fragment. 66282cd038dSYoshinobu Inoue * Just free other fragments. 66382cd038dSYoshinobu Inoue */ 66482cd038dSYoshinobu Inoue if (af6->ip6af_off == 0) { 66582cd038dSYoshinobu Inoue struct ip6_hdr *ip6; 66682cd038dSYoshinobu Inoue 66782cd038dSYoshinobu Inoue /* adjust pointer */ 66882cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 66982cd038dSYoshinobu Inoue 67006cd0a3fSHajimu UMEMOTO /* restore source and destination addresses */ 67182cd038dSYoshinobu Inoue ip6->ip6_src = q6->ip6q_src; 67282cd038dSYoshinobu Inoue ip6->ip6_dst = q6->ip6q_dst; 67382cd038dSYoshinobu Inoue 67482cd038dSYoshinobu Inoue icmp6_error(m, ICMP6_TIME_EXCEEDED, 67582cd038dSYoshinobu Inoue ICMP6_TIME_EXCEED_REASSEMBLY, 0); 676686cdd19SJun-ichiro itojun Hagino } else 67782cd038dSYoshinobu Inoue m_freem(m); 678686cdd19SJun-ichiro itojun Hagino free(af6, M_FTABLE); 67982cd038dSYoshinobu Inoue } 68082cd038dSYoshinobu Inoue frag6_remque(q6); 681603724d3SBjoern A. Zeeb V_frag6_nfrags -= q6->ip6q_nfrag; 6824b908c8bSRobert Watson #ifdef MAC 6834b908c8bSRobert Watson mac_ip6q_destroy(q6); 6844b908c8bSRobert Watson #endif 68582cd038dSYoshinobu Inoue free(q6, M_FTABLE); 686603724d3SBjoern A. Zeeb V_frag6_nfragpackets--; 68782cd038dSYoshinobu Inoue } 68882cd038dSYoshinobu Inoue 68982cd038dSYoshinobu Inoue /* 69082cd038dSYoshinobu Inoue * Put an ip fragment on a reassembly chain. 69182cd038dSYoshinobu Inoue * Like insque, but pointers in middle of structure. 69282cd038dSYoshinobu Inoue */ 69382cd038dSYoshinobu Inoue void 6941272577eSXin LI frag6_enq(struct ip6asfrag *af6, struct ip6asfrag *up6) 69582cd038dSYoshinobu Inoue { 6969888c401SHajimu UMEMOTO 6979bcf770cSHajimu UMEMOTO IP6Q_LOCK_ASSERT(); 6989888c401SHajimu UMEMOTO 69982cd038dSYoshinobu Inoue af6->ip6af_up = up6; 70082cd038dSYoshinobu Inoue af6->ip6af_down = up6->ip6af_down; 70182cd038dSYoshinobu Inoue up6->ip6af_down->ip6af_up = af6; 70282cd038dSYoshinobu Inoue up6->ip6af_down = af6; 70382cd038dSYoshinobu Inoue } 70482cd038dSYoshinobu Inoue 70582cd038dSYoshinobu Inoue /* 70682cd038dSYoshinobu Inoue * To frag6_enq as remque is to insque. 70782cd038dSYoshinobu Inoue */ 70882cd038dSYoshinobu Inoue void 7091272577eSXin LI frag6_deq(struct ip6asfrag *af6) 71082cd038dSYoshinobu Inoue { 7119888c401SHajimu UMEMOTO 7129bcf770cSHajimu UMEMOTO IP6Q_LOCK_ASSERT(); 7139888c401SHajimu UMEMOTO 71482cd038dSYoshinobu Inoue af6->ip6af_up->ip6af_down = af6->ip6af_down; 71582cd038dSYoshinobu Inoue af6->ip6af_down->ip6af_up = af6->ip6af_up; 71682cd038dSYoshinobu Inoue } 71782cd038dSYoshinobu Inoue 71882cd038dSYoshinobu Inoue void 7191272577eSXin LI frag6_insque(struct ip6q *new, struct ip6q *old) 72082cd038dSYoshinobu Inoue { 7219888c401SHajimu UMEMOTO 7229bcf770cSHajimu UMEMOTO IP6Q_LOCK_ASSERT(); 7239888c401SHajimu UMEMOTO 72482cd038dSYoshinobu Inoue new->ip6q_prev = old; 72582cd038dSYoshinobu Inoue new->ip6q_next = old->ip6q_next; 72682cd038dSYoshinobu Inoue old->ip6q_next->ip6q_prev= new; 72782cd038dSYoshinobu Inoue old->ip6q_next = new; 72882cd038dSYoshinobu Inoue } 72982cd038dSYoshinobu Inoue 73082cd038dSYoshinobu Inoue void 7311272577eSXin LI frag6_remque(struct ip6q *p6) 73282cd038dSYoshinobu Inoue { 7339888c401SHajimu UMEMOTO 7349bcf770cSHajimu UMEMOTO IP6Q_LOCK_ASSERT(); 7359888c401SHajimu UMEMOTO 73682cd038dSYoshinobu Inoue p6->ip6q_prev->ip6q_next = p6->ip6q_next; 73782cd038dSYoshinobu Inoue p6->ip6q_next->ip6q_prev = p6->ip6q_prev; 73882cd038dSYoshinobu Inoue } 73982cd038dSYoshinobu Inoue 74082cd038dSYoshinobu Inoue /* 74133841545SHajimu UMEMOTO * IPv6 reassembling timer processing; 74282cd038dSYoshinobu Inoue * if a timer expires on a reassembly 74382cd038dSYoshinobu Inoue * queue, discard it. 74482cd038dSYoshinobu Inoue */ 74582cd038dSYoshinobu Inoue void 7461272577eSXin LI frag6_slowtimo(void) 74782cd038dSYoshinobu Inoue { 7488b615593SMarko Zec VNET_ITERATOR_DECL(vnet_iter); 74982cd038dSYoshinobu Inoue struct ip6q *q6; 75082cd038dSYoshinobu Inoue 7515ee847d3SRobert Watson VNET_LIST_RLOCK_NOSLEEP(); 7529888c401SHajimu UMEMOTO IP6Q_LOCK(); 7538b615593SMarko Zec VNET_FOREACH(vnet_iter) { 7548b615593SMarko Zec CURVNET_SET(vnet_iter); 755603724d3SBjoern A. Zeeb q6 = V_ip6q.ip6q_next; 75682cd038dSYoshinobu Inoue if (q6) 757603724d3SBjoern A. Zeeb while (q6 != &V_ip6q) { 75882cd038dSYoshinobu Inoue --q6->ip6q_ttl; 75982cd038dSYoshinobu Inoue q6 = q6->ip6q_next; 76082cd038dSYoshinobu Inoue if (q6->ip6q_prev->ip6q_ttl == 0) { 7619cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_fragtimeout); 76282cd038dSYoshinobu Inoue /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ 76382cd038dSYoshinobu Inoue frag6_freef(q6->ip6q_prev); 76482cd038dSYoshinobu Inoue } 76582cd038dSYoshinobu Inoue } 76682cd038dSYoshinobu Inoue /* 76782cd038dSYoshinobu Inoue * If we are over the maximum number of fragments 76882cd038dSYoshinobu Inoue * (due to the limit being lowered), drain off 76982cd038dSYoshinobu Inoue * enough to get down to the new limit. 77082cd038dSYoshinobu Inoue */ 771603724d3SBjoern A. Zeeb while (V_frag6_nfragpackets > (u_int)V_ip6_maxfragpackets && 772603724d3SBjoern A. Zeeb V_ip6q.ip6q_prev) { 7739cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_fragoverflow); 77482cd038dSYoshinobu Inoue /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ 775603724d3SBjoern A. Zeeb frag6_freef(V_ip6q.ip6q_prev); 77682cd038dSYoshinobu Inoue } 7778b615593SMarko Zec CURVNET_RESTORE(); 7788b615593SMarko Zec } 7799888c401SHajimu UMEMOTO IP6Q_UNLOCK(); 7805ee847d3SRobert Watson VNET_LIST_RUNLOCK_NOSLEEP(); 78182cd038dSYoshinobu Inoue } 78282cd038dSYoshinobu Inoue 78382cd038dSYoshinobu Inoue /* 78482cd038dSYoshinobu Inoue * Drain off all datagram fragments. 78582cd038dSYoshinobu Inoue */ 78682cd038dSYoshinobu Inoue void 7871272577eSXin LI frag6_drain(void) 78882cd038dSYoshinobu Inoue { 7898b615593SMarko Zec VNET_ITERATOR_DECL(vnet_iter); 7909888c401SHajimu UMEMOTO 7915ee847d3SRobert Watson VNET_LIST_RLOCK_NOSLEEP(); 7925ee847d3SRobert Watson if (IP6Q_TRYLOCK() == 0) { 7935ee847d3SRobert Watson VNET_LIST_RUNLOCK_NOSLEEP(); 79482cd038dSYoshinobu Inoue return; 7955ee847d3SRobert Watson } 7968b615593SMarko Zec VNET_FOREACH(vnet_iter) { 7978b615593SMarko Zec CURVNET_SET(vnet_iter); 798603724d3SBjoern A. Zeeb while (V_ip6q.ip6q_next != &V_ip6q) { 7999cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_fragdropped); 80082cd038dSYoshinobu Inoue /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ 801603724d3SBjoern A. Zeeb frag6_freef(V_ip6q.ip6q_next); 80282cd038dSYoshinobu Inoue } 8038b615593SMarko Zec CURVNET_RESTORE(); 8048b615593SMarko Zec } 8059888c401SHajimu UMEMOTO IP6Q_UNLOCK(); 8065ee847d3SRobert Watson VNET_LIST_RUNLOCK_NOSLEEP(); 80782cd038dSYoshinobu Inoue } 808e5ee7060SGleb Smirnoff 809e5ee7060SGleb Smirnoff int 810e5ee7060SGleb Smirnoff ip6_deletefraghdr(struct mbuf *m, int offset, int wait) 811e5ee7060SGleb Smirnoff { 812e5ee7060SGleb Smirnoff struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 813e5ee7060SGleb Smirnoff struct mbuf *t; 814e5ee7060SGleb Smirnoff 815e5ee7060SGleb Smirnoff /* Delete frag6 header. */ 816e5ee7060SGleb Smirnoff if (m->m_len >= offset + sizeof(struct ip6_frag)) { 817e5ee7060SGleb Smirnoff /* This is the only possible case with !PULLDOWN_TEST. */ 818e5ee7060SGleb Smirnoff bcopy(ip6, (char *)ip6 + sizeof(struct ip6_frag), 819e5ee7060SGleb Smirnoff offset); 820e5ee7060SGleb Smirnoff m->m_data += sizeof(struct ip6_frag); 821e5ee7060SGleb Smirnoff m->m_len -= sizeof(struct ip6_frag); 822e5ee7060SGleb Smirnoff } else { 823e5ee7060SGleb Smirnoff /* This comes with no copy if the boundary is on cluster. */ 824e5ee7060SGleb Smirnoff if ((t = m_split(m, offset, wait)) == NULL) 825e5ee7060SGleb Smirnoff return (ENOMEM); 826e5ee7060SGleb Smirnoff m_adj(t, sizeof(struct ip6_frag)); 827e5ee7060SGleb Smirnoff m_cat(m, t); 828e5ee7060SGleb Smirnoff } 829e5ee7060SGleb Smirnoff 830e5ee7060SGleb Smirnoff return (0); 831e5ee7060SGleb Smirnoff } 832