xref: /freebsd/sys/netinet/ip_reass.c (revision 3d0d5b21c9b2cfd7e48209340a090f63ca4bce03)
11dbefcc0SGleb Smirnoff /*-
21dbefcc0SGleb Smirnoff  * Copyright (c) 2015 Gleb Smirnoff <glebius@FreeBSD.org>
31dbefcc0SGleb Smirnoff  * Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org>
41dbefcc0SGleb Smirnoff  * Copyright (c) 1982, 1986, 1988, 1993
51dbefcc0SGleb Smirnoff  *	The Regents of the University of California.  All rights reserved.
61dbefcc0SGleb Smirnoff  *
71dbefcc0SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
81dbefcc0SGleb Smirnoff  * modification, are permitted provided that the following conditions
91dbefcc0SGleb Smirnoff  * are met:
101dbefcc0SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
111dbefcc0SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
121dbefcc0SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
131dbefcc0SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
141dbefcc0SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
161dbefcc0SGleb Smirnoff  *    may be used to endorse or promote products derived from this software
171dbefcc0SGleb Smirnoff  *    without specific prior written permission.
181dbefcc0SGleb Smirnoff  *
191dbefcc0SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
201dbefcc0SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
211dbefcc0SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
221dbefcc0SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
231dbefcc0SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
241dbefcc0SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
251dbefcc0SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
261dbefcc0SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
271dbefcc0SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
281dbefcc0SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
291dbefcc0SGleb Smirnoff  * SUCH DAMAGE.
301dbefcc0SGleb Smirnoff  *
311dbefcc0SGleb Smirnoff  *	@(#)ip_input.c	8.2 (Berkeley) 1/4/94
321dbefcc0SGleb Smirnoff  */
331dbefcc0SGleb Smirnoff 
341dbefcc0SGleb Smirnoff #include <sys/cdefs.h>
351dbefcc0SGleb Smirnoff __FBSDID("$FreeBSD$");
361dbefcc0SGleb Smirnoff 
371dbefcc0SGleb Smirnoff #include "opt_rss.h"
381dbefcc0SGleb Smirnoff 
391dbefcc0SGleb Smirnoff #include <sys/param.h>
401dbefcc0SGleb Smirnoff #include <sys/systm.h>
411dbefcc0SGleb Smirnoff #include <sys/eventhandler.h>
4204f44499SHans Petter Selasky #include <sys/kernel.h>
43c047fd1bSGleb Smirnoff #include <sys/hash.h>
441dbefcc0SGleb Smirnoff #include <sys/mbuf.h>
451dbefcc0SGleb Smirnoff #include <sys/malloc.h>
46ff790bbaSJonathan T. Looney #include <sys/limits.h>
471dbefcc0SGleb Smirnoff #include <sys/lock.h>
481dbefcc0SGleb Smirnoff #include <sys/mutex.h>
491dbefcc0SGleb Smirnoff #include <sys/sysctl.h>
50a55383e7SHans Petter Selasky #include <sys/socket.h>
511dbefcc0SGleb Smirnoff 
52a55383e7SHans Petter Selasky #include <net/if.h>
53a55383e7SHans Petter Selasky #include <net/if_var.h>
54*3d0d5b21SJustin Hibbits #include <net/if_private.h>
551dbefcc0SGleb Smirnoff #include <net/rss_config.h>
563e217461SAdrian Chadd #include <net/netisr.h>
571dbefcc0SGleb Smirnoff #include <net/vnet.h>
581dbefcc0SGleb Smirnoff 
591dbefcc0SGleb Smirnoff #include <netinet/in.h>
601dbefcc0SGleb Smirnoff #include <netinet/ip.h>
611dbefcc0SGleb Smirnoff #include <netinet/ip_var.h>
621dbefcc0SGleb Smirnoff #include <netinet/in_rss.h>
631dbefcc0SGleb Smirnoff #ifdef MAC
641dbefcc0SGleb Smirnoff #include <security/mac/mac_framework.h>
651dbefcc0SGleb Smirnoff #endif
661dbefcc0SGleb Smirnoff 
671dbefcc0SGleb Smirnoff SYSCTL_DECL(_net_inet_ip);
681dbefcc0SGleb Smirnoff 
691dbefcc0SGleb Smirnoff /*
701dbefcc0SGleb Smirnoff  * Reassembly headers are stored in hash buckets.
711dbefcc0SGleb Smirnoff  */
72a967df1cSJonathan T. Looney #define	IPREASS_NHASH_LOG2	10
731dbefcc0SGleb Smirnoff #define	IPREASS_NHASH		(1 << IPREASS_NHASH_LOG2)
74c8bc8741SGleb Smirnoff #define	IPREASS_HMASK		(V_ipq_hashsize - 1)
751dbefcc0SGleb Smirnoff 
761dbefcc0SGleb Smirnoff struct ipqbucket {
771dbefcc0SGleb Smirnoff 	TAILQ_HEAD(ipqhead, ipq) head;
781dbefcc0SGleb Smirnoff 	struct mtx		 lock;
79a30cb315SGleb Smirnoff 	struct callout		 timer;
80a30cb315SGleb Smirnoff #ifdef VIMAGE
81a30cb315SGleb Smirnoff 	struct vnet		 *vnet;
82a30cb315SGleb Smirnoff #endif
83ff790bbaSJonathan T. Looney 	int			 count;
841dbefcc0SGleb Smirnoff };
851dbefcc0SGleb Smirnoff 
861494f477SGleb Smirnoff VNET_DEFINE_STATIC(struct ipqbucket *, ipq);
871dbefcc0SGleb Smirnoff #define	V_ipq		VNET(ipq)
885f901c92SAndrew Turner VNET_DEFINE_STATIC(uint32_t, ipq_hashseed);
89c047fd1bSGleb Smirnoff #define	V_ipq_hashseed	VNET(ipq_hashseed)
901494f477SGleb Smirnoff VNET_DEFINE_STATIC(uint32_t, ipq_hashsize);
911494f477SGleb Smirnoff #define	V_ipq_hashsize	VNET(ipq_hashsize)
921dbefcc0SGleb Smirnoff 
931dbefcc0SGleb Smirnoff #define	IPQ_LOCK(i)	mtx_lock(&V_ipq[i].lock)
941dbefcc0SGleb Smirnoff #define	IPQ_TRYLOCK(i)	mtx_trylock(&V_ipq[i].lock)
951dbefcc0SGleb Smirnoff #define	IPQ_UNLOCK(i)	mtx_unlock(&V_ipq[i].lock)
961dbefcc0SGleb Smirnoff #define	IPQ_LOCK_ASSERT(i)	mtx_assert(&V_ipq[i].lock, MA_OWNED)
97a30cb315SGleb Smirnoff #define	IPQ_BUCKET_LOCK_ASSERT(b)	mtx_assert(&(b)->lock, MA_OWNED)
981dbefcc0SGleb Smirnoff 
99ff790bbaSJonathan T. Looney VNET_DEFINE_STATIC(int, ipreass_maxbucketsize);
100ff790bbaSJonathan T. Looney #define	V_ipreass_maxbucketsize	VNET(ipreass_maxbucketsize)
101ff790bbaSJonathan T. Looney 
1021dbefcc0SGleb Smirnoff void		ipreass_init(void);
103aea0cd04SGleb Smirnoff void		ipreass_vnet_init(void);
1041dbefcc0SGleb Smirnoff #ifdef VIMAGE
1051dbefcc0SGleb Smirnoff void		ipreass_destroy(void);
1061dbefcc0SGleb Smirnoff #endif
1071dbefcc0SGleb Smirnoff static int	sysctl_maxfragpackets(SYSCTL_HANDLER_ARGS);
108ff790bbaSJonathan T. Looney static int	sysctl_maxfragbucketsize(SYSCTL_HANDLER_ARGS);
109a30cb315SGleb Smirnoff static int	sysctl_fragttl(SYSCTL_HANDLER_ARGS);
1101dbefcc0SGleb Smirnoff static void	ipreass_zone_change(void *);
1111dbefcc0SGleb Smirnoff static void	ipreass_drain_tomax(void);
112ff790bbaSJonathan T. Looney static void	ipq_free(struct ipqbucket *, struct ipq *);
1131dbefcc0SGleb Smirnoff static struct ipq * ipq_reuse(int);
114a30cb315SGleb Smirnoff static void	ipreass_callout(void *);
115a30cb315SGleb Smirnoff static void	ipreass_reschedule(struct ipqbucket *);
1161dbefcc0SGleb Smirnoff 
1171dbefcc0SGleb Smirnoff static inline void
118ff790bbaSJonathan T. Looney ipq_timeout(struct ipqbucket *bucket, struct ipq *fp)
1191dbefcc0SGleb Smirnoff {
1201dbefcc0SGleb Smirnoff 
1211dbefcc0SGleb Smirnoff 	IPSTAT_ADD(ips_fragtimeout, fp->ipq_nfrags);
122ff790bbaSJonathan T. Looney 	ipq_free(bucket, fp);
1231dbefcc0SGleb Smirnoff }
1241dbefcc0SGleb Smirnoff 
1251dbefcc0SGleb Smirnoff static inline void
126ff790bbaSJonathan T. Looney ipq_drop(struct ipqbucket *bucket, struct ipq *fp)
1271dbefcc0SGleb Smirnoff {
1281dbefcc0SGleb Smirnoff 
1291dbefcc0SGleb Smirnoff 	IPSTAT_ADD(ips_fragdropped, fp->ipq_nfrags);
130ff790bbaSJonathan T. Looney 	ipq_free(bucket, fp);
131a30cb315SGleb Smirnoff 	ipreass_reschedule(bucket);
1321dbefcc0SGleb Smirnoff }
1331dbefcc0SGleb Smirnoff 
134a967df1cSJonathan T. Looney /*
135a967df1cSJonathan T. Looney  * By default, limit the number of IP fragments across all reassembly
136a967df1cSJonathan T. Looney  * queues to  1/32 of the total number of mbuf clusters.
137a967df1cSJonathan T. Looney  *
138a967df1cSJonathan T. Looney  * Limit the total number of reassembly queues per VNET to the
139a967df1cSJonathan T. Looney  * IP fragment limit, but ensure the limit will not allow any bucket
140a967df1cSJonathan T. Looney  * to grow above 100 items. (The bucket limit is
1411494f477SGleb Smirnoff  * IP_MAXFRAGPACKETS / (V_ipq_hashsize / 2), so the 50 is the correct
142a967df1cSJonathan T. Looney  * multiplier to reach a 100-item limit.)
143a967df1cSJonathan T. Looney  * The 100-item limit was chosen as brief testing seems to show that
144a967df1cSJonathan T. Looney  * this produces "reasonable" performance on some subset of systems
145a967df1cSJonathan T. Looney  * under DoS attack.
146a967df1cSJonathan T. Looney  */
147a967df1cSJonathan T. Looney #define	IP_MAXFRAGS		(nmbclusters / 32)
1481494f477SGleb Smirnoff #define	IP_MAXFRAGPACKETS	(imin(IP_MAXFRAGS, V_ipq_hashsize * 50))
149a967df1cSJonathan T. Looney 
1507b9c5eb0SJonathan T. Looney static int		maxfrags;
151d2b95af1SMateusz Guzik static u_int __exclusive_cache_line	nfrags;
1527b9c5eb0SJonathan T. Looney SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfrags, CTLFLAG_RW,
1537b9c5eb0SJonathan T. Looney     &maxfrags, 0,
1547b9c5eb0SJonathan T. Looney     "Maximum number of IPv4 fragments allowed across all reassembly queues");
1557b9c5eb0SJonathan T. Looney SYSCTL_UINT(_net_inet_ip, OID_AUTO, curfrags, CTLFLAG_RD,
156d2b95af1SMateusz Guzik     &nfrags, 0,
1577b9c5eb0SJonathan T. Looney     "Current number of IPv4 fragments across all reassembly queues");
1587b9c5eb0SJonathan T. Looney 
1595f901c92SAndrew Turner VNET_DEFINE_STATIC(uma_zone_t, ipq_zone);
1601dbefcc0SGleb Smirnoff #define	V_ipq_zone	VNET(ipq_zone)
1611494f477SGleb Smirnoff 
1621494f477SGleb Smirnoff SYSCTL_UINT(_net_inet_ip, OID_AUTO, reass_hashsize,
1631494f477SGleb Smirnoff     CTLFLAG_VNET | CTLFLAG_RDTUN, &VNET_NAME(ipq_hashsize), 0,
1641494f477SGleb Smirnoff     "Size of IP fragment reassembly hashtable");
1651494f477SGleb Smirnoff 
1667029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_ip, OID_AUTO, maxfragpackets,
1677029da5cSPawel Biernacki     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
1687029da5cSPawel Biernacki     NULL, 0, sysctl_maxfragpackets, "I",
1691dbefcc0SGleb Smirnoff     "Maximum number of IPv4 fragment reassembly queue entries");
1701dbefcc0SGleb Smirnoff SYSCTL_UMA_CUR(_net_inet_ip, OID_AUTO, fragpackets, CTLFLAG_VNET,
1711dbefcc0SGleb Smirnoff     &VNET_NAME(ipq_zone),
1721dbefcc0SGleb Smirnoff     "Current number of IPv4 fragment reassembly queue entries");
1731dbefcc0SGleb Smirnoff 
1745f901c92SAndrew Turner VNET_DEFINE_STATIC(int, noreass);
1751dbefcc0SGleb Smirnoff #define	V_noreass	VNET(noreass)
1761dbefcc0SGleb Smirnoff 
1775f901c92SAndrew Turner VNET_DEFINE_STATIC(int, maxfragsperpacket);
1781dbefcc0SGleb Smirnoff #define	V_maxfragsperpacket	VNET(maxfragsperpacket)
1791dbefcc0SGleb Smirnoff SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfragsperpacket, CTLFLAG_VNET | CTLFLAG_RW,
1801dbefcc0SGleb Smirnoff     &VNET_NAME(maxfragsperpacket), 0,
1811dbefcc0SGleb Smirnoff     "Maximum number of IPv4 fragments allowed per packet");
182ff790bbaSJonathan T. Looney SYSCTL_PROC(_net_inet_ip, OID_AUTO, maxfragbucketsize,
183ff790bbaSJonathan T. Looney     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, NULL, 0,
184ff790bbaSJonathan T. Looney     sysctl_maxfragbucketsize, "I",
185ff790bbaSJonathan T. Looney     "Maximum number of IPv4 fragment reassembly queue entries per bucket");
1861dbefcc0SGleb Smirnoff 
187a30cb315SGleb Smirnoff VNET_DEFINE_STATIC(u_int, ipfragttl) = 30;
188a30cb315SGleb Smirnoff #define	V_ipfragttl	VNET(ipfragttl)
189a30cb315SGleb Smirnoff SYSCTL_PROC(_net_inet_ip, OID_AUTO, fragttl, CTLTYPE_INT | CTLFLAG_RW |
190a30cb315SGleb Smirnoff     CTLFLAG_MPSAFE | CTLFLAG_VNET, NULL, 0, sysctl_fragttl, "IU",
191a30cb315SGleb Smirnoff     "IP fragment life time on reassembly queue (seconds)");
1928338690aSGleb Smirnoff 
1931dbefcc0SGleb Smirnoff /*
1941dbefcc0SGleb Smirnoff  * Take incoming datagram fragment and try to reassemble it into
1951dbefcc0SGleb Smirnoff  * whole datagram.  If the argument is the first fragment or one
1961dbefcc0SGleb Smirnoff  * in between the function will return NULL and store the mbuf
1971dbefcc0SGleb Smirnoff  * in the fragment chain.  If the argument is the last fragment
1981dbefcc0SGleb Smirnoff  * the packet will be reassembled and the pointer to the new
1991dbefcc0SGleb Smirnoff  * mbuf returned for further processing.  Only m_tags attached
2001dbefcc0SGleb Smirnoff  * to the first packet/fragment are preserved.
2011dbefcc0SGleb Smirnoff  * The IP header is *NOT* adjusted out of iplen.
2021dbefcc0SGleb Smirnoff  */
2031dbefcc0SGleb Smirnoff #define	M_IP_FRAG	M_PROTO9
2041dbefcc0SGleb Smirnoff struct mbuf *
2051dbefcc0SGleb Smirnoff ip_reass(struct mbuf *m)
2061dbefcc0SGleb Smirnoff {
2071dbefcc0SGleb Smirnoff 	struct ip *ip;
2081dbefcc0SGleb Smirnoff 	struct mbuf *p, *q, *nq, *t;
2091dbefcc0SGleb Smirnoff 	struct ipq *fp;
210a55383e7SHans Petter Selasky 	struct ifnet *srcifp;
2111dbefcc0SGleb Smirnoff 	struct ipqhead *head;
2127b9c5eb0SJonathan T. Looney 	int i, hlen, next, tmpmax;
2131dbefcc0SGleb Smirnoff 	u_int8_t ecn, ecn0;
2145d9bd455SJonathan T. Looney 	uint32_t hash, hashkey[3];
2151dbefcc0SGleb Smirnoff #ifdef	RSS
2161dbefcc0SGleb Smirnoff 	uint32_t rss_hash, rss_type;
2171dbefcc0SGleb Smirnoff #endif
2181dbefcc0SGleb Smirnoff 
2191dbefcc0SGleb Smirnoff 	/*
2201dbefcc0SGleb Smirnoff 	 * If no reassembling or maxfragsperpacket are 0,
2211dbefcc0SGleb Smirnoff 	 * never accept fragments.
2227b9c5eb0SJonathan T. Looney 	 * Also, drop packet if it would exceed the maximum
2237b9c5eb0SJonathan T. Looney 	 * number of fragments.
2241dbefcc0SGleb Smirnoff 	 */
2257b9c5eb0SJonathan T. Looney 	tmpmax = maxfrags;
2267b9c5eb0SJonathan T. Looney 	if (V_noreass == 1 || V_maxfragsperpacket == 0 ||
2277b9c5eb0SJonathan T. Looney 	    (tmpmax >= 0 && atomic_load_int(&nfrags) >= (u_int)tmpmax)) {
2281dbefcc0SGleb Smirnoff 		IPSTAT_INC(ips_fragments);
2291dbefcc0SGleb Smirnoff 		IPSTAT_INC(ips_fragdropped);
2301dbefcc0SGleb Smirnoff 		m_freem(m);
2311dbefcc0SGleb Smirnoff 		return (NULL);
2321dbefcc0SGleb Smirnoff 	}
2331dbefcc0SGleb Smirnoff 
2341dbefcc0SGleb Smirnoff 	ip = mtod(m, struct ip *);
2351dbefcc0SGleb Smirnoff 	hlen = ip->ip_hl << 2;
2361dbefcc0SGleb Smirnoff 
2371dbefcc0SGleb Smirnoff 	/*
2381dbefcc0SGleb Smirnoff 	 * Adjust ip_len to not reflect header,
2391dbefcc0SGleb Smirnoff 	 * convert offset of this to bytes.
2401dbefcc0SGleb Smirnoff 	 */
2411dbefcc0SGleb Smirnoff 	ip->ip_len = htons(ntohs(ip->ip_len) - hlen);
2421dbefcc0SGleb Smirnoff 	/*
2431dbefcc0SGleb Smirnoff 	 * Make sure that fragments have a data length
2442157f3c3SJonathan T. Looney 	 * that's a non-zero multiple of 8 bytes, unless
2452157f3c3SJonathan T. Looney 	 * this is the last fragment.
2461dbefcc0SGleb Smirnoff 	 */
2472157f3c3SJonathan T. Looney 	if (ip->ip_len == htons(0) ||
2482157f3c3SJonathan T. Looney 	    ((ip->ip_off & htons(IP_MF)) && (ntohs(ip->ip_len) & 0x7) != 0)) {
2491dbefcc0SGleb Smirnoff 		IPSTAT_INC(ips_toosmall); /* XXX */
2501dbefcc0SGleb Smirnoff 		IPSTAT_INC(ips_fragdropped);
2511dbefcc0SGleb Smirnoff 		m_freem(m);
2521dbefcc0SGleb Smirnoff 		return (NULL);
2531dbefcc0SGleb Smirnoff 	}
2542157f3c3SJonathan T. Looney 	if (ip->ip_off & htons(IP_MF))
2551dbefcc0SGleb Smirnoff 		m->m_flags |= M_IP_FRAG;
2562157f3c3SJonathan T. Looney 	else
2571dbefcc0SGleb Smirnoff 		m->m_flags &= ~M_IP_FRAG;
2581dbefcc0SGleb Smirnoff 	ip->ip_off = htons(ntohs(ip->ip_off) << 3);
2591dbefcc0SGleb Smirnoff 
2601dbefcc0SGleb Smirnoff 	/*
26186af1d02SMark Johnston 	 * Make sure the fragment lies within a packet of valid size.
26286af1d02SMark Johnston 	 */
26386af1d02SMark Johnston 	if (ntohs(ip->ip_len) + ntohs(ip->ip_off) > IP_MAXPACKET) {
26486af1d02SMark Johnston 		IPSTAT_INC(ips_toolong);
26586af1d02SMark Johnston 		IPSTAT_INC(ips_fragdropped);
26686af1d02SMark Johnston 		m_freem(m);
26786af1d02SMark Johnston 		return (NULL);
26886af1d02SMark Johnston 	}
26986af1d02SMark Johnston 
27086af1d02SMark Johnston 	/*
271a55383e7SHans Petter Selasky 	 * Store receive network interface pointer for later.
272a55383e7SHans Petter Selasky 	 */
273a55383e7SHans Petter Selasky 	srcifp = m->m_pkthdr.rcvif;
274a55383e7SHans Petter Selasky 
275a55383e7SHans Petter Selasky 	/*
2761dbefcc0SGleb Smirnoff 	 * Attempt reassembly; if it succeeds, proceed.
2771dbefcc0SGleb Smirnoff 	 * ip_reass() will return a different mbuf.
2781dbefcc0SGleb Smirnoff 	 */
2791dbefcc0SGleb Smirnoff 	IPSTAT_INC(ips_fragments);
2801dbefcc0SGleb Smirnoff 	m->m_pkthdr.PH_loc.ptr = ip;
2811dbefcc0SGleb Smirnoff 
2821dbefcc0SGleb Smirnoff 	/*
2831dbefcc0SGleb Smirnoff 	 * Presence of header sizes in mbufs
2841dbefcc0SGleb Smirnoff 	 * would confuse code below.
2851dbefcc0SGleb Smirnoff 	 */
2861dbefcc0SGleb Smirnoff 	m->m_data += hlen;
2871dbefcc0SGleb Smirnoff 	m->m_len -= hlen;
2881dbefcc0SGleb Smirnoff 
2895d9bd455SJonathan T. Looney 	hashkey[0] = ip->ip_src.s_addr;
2905d9bd455SJonathan T. Looney 	hashkey[1] = ip->ip_dst.s_addr;
2915d9bd455SJonathan T. Looney 	hashkey[2] = (uint32_t)ip->ip_p << 16;
2925d9bd455SJonathan T. Looney 	hashkey[2] += ip->ip_id;
2935d9bd455SJonathan T. Looney 	hash = jenkins_hash32(hashkey, nitems(hashkey), V_ipq_hashseed);
2945d9bd455SJonathan T. Looney 	hash &= IPREASS_HMASK;
2951dbefcc0SGleb Smirnoff 	head = &V_ipq[hash].head;
2961dbefcc0SGleb Smirnoff 	IPQ_LOCK(hash);
2971dbefcc0SGleb Smirnoff 
2981dbefcc0SGleb Smirnoff 	/*
2991dbefcc0SGleb Smirnoff 	 * Look for queue of fragments
3001dbefcc0SGleb Smirnoff 	 * of this datagram.
3011dbefcc0SGleb Smirnoff 	 */
3021dbefcc0SGleb Smirnoff 	TAILQ_FOREACH(fp, head, ipq_list)
3031dbefcc0SGleb Smirnoff 		if (ip->ip_id == fp->ipq_id &&
3041dbefcc0SGleb Smirnoff 		    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
3051dbefcc0SGleb Smirnoff 		    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
3061dbefcc0SGleb Smirnoff #ifdef MAC
3071dbefcc0SGleb Smirnoff 		    mac_ipq_match(m, fp) &&
3081dbefcc0SGleb Smirnoff #endif
3091dbefcc0SGleb Smirnoff 		    ip->ip_p == fp->ipq_p)
3101dbefcc0SGleb Smirnoff 			break;
3111dbefcc0SGleb Smirnoff 	/*
3121dbefcc0SGleb Smirnoff 	 * If first fragment to arrive, create a reassembly queue.
3131dbefcc0SGleb Smirnoff 	 */
3141dbefcc0SGleb Smirnoff 	if (fp == NULL) {
315ff790bbaSJonathan T. Looney 		if (V_ipq[hash].count < V_ipreass_maxbucketsize)
3161dbefcc0SGleb Smirnoff 			fp = uma_zalloc(V_ipq_zone, M_NOWAIT);
3171dbefcc0SGleb Smirnoff 		if (fp == NULL)
3181dbefcc0SGleb Smirnoff 			fp = ipq_reuse(hash);
319ff790bbaSJonathan T. Looney 		if (fp == NULL)
320ff790bbaSJonathan T. Looney 			goto dropfrag;
3211dbefcc0SGleb Smirnoff #ifdef MAC
3221dbefcc0SGleb Smirnoff 		if (mac_ipq_init(fp, M_NOWAIT) != 0) {
3231dbefcc0SGleb Smirnoff 			uma_zfree(V_ipq_zone, fp);
3241dbefcc0SGleb Smirnoff 			fp = NULL;
3251dbefcc0SGleb Smirnoff 			goto dropfrag;
3261dbefcc0SGleb Smirnoff 		}
3271dbefcc0SGleb Smirnoff 		mac_ipq_create(m, fp);
3281dbefcc0SGleb Smirnoff #endif
3291dbefcc0SGleb Smirnoff 		TAILQ_INSERT_HEAD(head, fp, ipq_list);
330ff790bbaSJonathan T. Looney 		V_ipq[hash].count++;
3311dbefcc0SGleb Smirnoff 		fp->ipq_nfrags = 1;
3327b9c5eb0SJonathan T. Looney 		atomic_add_int(&nfrags, 1);
333a30cb315SGleb Smirnoff 		fp->ipq_expire = time_uptime + V_ipfragttl;
3341dbefcc0SGleb Smirnoff 		fp->ipq_p = ip->ip_p;
3351dbefcc0SGleb Smirnoff 		fp->ipq_id = ip->ip_id;
3361dbefcc0SGleb Smirnoff 		fp->ipq_src = ip->ip_src;
3371dbefcc0SGleb Smirnoff 		fp->ipq_dst = ip->ip_dst;
3381dbefcc0SGleb Smirnoff 		fp->ipq_frags = m;
3392157f3c3SJonathan T. Looney 		if (m->m_flags & M_IP_FRAG)
3402157f3c3SJonathan T. Looney 			fp->ipq_maxoff = -1;
3412157f3c3SJonathan T. Looney 		else
3422157f3c3SJonathan T. Looney 			fp->ipq_maxoff = ntohs(ip->ip_off) + ntohs(ip->ip_len);
3431dbefcc0SGleb Smirnoff 		m->m_nextpkt = NULL;
344a30cb315SGleb Smirnoff 		if (fp == TAILQ_LAST(head, ipqhead))
345a30cb315SGleb Smirnoff 			callout_reset_sbt(&V_ipq[hash].timer,
346a30cb315SGleb Smirnoff 			    SBT_1S * V_ipfragttl, SBT_1S, ipreass_callout,
347a30cb315SGleb Smirnoff 			    &V_ipq[hash], 0);
348a30cb315SGleb Smirnoff 		else
349a30cb315SGleb Smirnoff 			MPASS(callout_active(&V_ipq[hash].timer));
3501dbefcc0SGleb Smirnoff 		goto done;
3511dbefcc0SGleb Smirnoff 	} else {
3522157f3c3SJonathan T. Looney 		/*
3532157f3c3SJonathan T. Looney 		 * If we already saw the last fragment, make sure
3542157f3c3SJonathan T. Looney 		 * this fragment's offset looks sane. Otherwise, if
3552157f3c3SJonathan T. Looney 		 * this is the last fragment, record its endpoint.
3562157f3c3SJonathan T. Looney 		 */
3572157f3c3SJonathan T. Looney 		if (fp->ipq_maxoff > 0) {
3582157f3c3SJonathan T. Looney 			i = ntohs(ip->ip_off) + ntohs(ip->ip_len);
3592157f3c3SJonathan T. Looney 			if (((m->m_flags & M_IP_FRAG) && i >= fp->ipq_maxoff) ||
3602157f3c3SJonathan T. Looney 			    ((m->m_flags & M_IP_FRAG) == 0 &&
3612157f3c3SJonathan T. Looney 			    i != fp->ipq_maxoff)) {
3622157f3c3SJonathan T. Looney 				fp = NULL;
3632157f3c3SJonathan T. Looney 				goto dropfrag;
3642157f3c3SJonathan T. Looney 			}
3652157f3c3SJonathan T. Looney 		} else if ((m->m_flags & M_IP_FRAG) == 0)
3662157f3c3SJonathan T. Looney 			fp->ipq_maxoff = ntohs(ip->ip_off) + ntohs(ip->ip_len);
3671dbefcc0SGleb Smirnoff 		fp->ipq_nfrags++;
3687b9c5eb0SJonathan T. Looney 		atomic_add_int(&nfrags, 1);
3691dbefcc0SGleb Smirnoff #ifdef MAC
3701dbefcc0SGleb Smirnoff 		mac_ipq_update(m, fp);
3711dbefcc0SGleb Smirnoff #endif
3721dbefcc0SGleb Smirnoff 	}
3731dbefcc0SGleb Smirnoff 
3741dbefcc0SGleb Smirnoff #define GETIP(m)	((struct ip*)((m)->m_pkthdr.PH_loc.ptr))
3751dbefcc0SGleb Smirnoff 
3761dbefcc0SGleb Smirnoff 	/*
3771dbefcc0SGleb Smirnoff 	 * Handle ECN by comparing this segment with the first one;
3781dbefcc0SGleb Smirnoff 	 * if CE is set, do not lose CE.
3791dbefcc0SGleb Smirnoff 	 * drop if CE and not-ECT are mixed for the same packet.
3801dbefcc0SGleb Smirnoff 	 */
3811dbefcc0SGleb Smirnoff 	ecn = ip->ip_tos & IPTOS_ECN_MASK;
3821dbefcc0SGleb Smirnoff 	ecn0 = GETIP(fp->ipq_frags)->ip_tos & IPTOS_ECN_MASK;
3831dbefcc0SGleb Smirnoff 	if (ecn == IPTOS_ECN_CE) {
3841dbefcc0SGleb Smirnoff 		if (ecn0 == IPTOS_ECN_NOTECT)
3851dbefcc0SGleb Smirnoff 			goto dropfrag;
3861dbefcc0SGleb Smirnoff 		if (ecn0 != IPTOS_ECN_CE)
3871dbefcc0SGleb Smirnoff 			GETIP(fp->ipq_frags)->ip_tos |= IPTOS_ECN_CE;
3881dbefcc0SGleb Smirnoff 	}
3891dbefcc0SGleb Smirnoff 	if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT)
3901dbefcc0SGleb Smirnoff 		goto dropfrag;
3911dbefcc0SGleb Smirnoff 
3921dbefcc0SGleb Smirnoff 	/*
3931dbefcc0SGleb Smirnoff 	 * Find a segment which begins after this one does.
3941dbefcc0SGleb Smirnoff 	 */
3951dbefcc0SGleb Smirnoff 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
3961dbefcc0SGleb Smirnoff 		if (ntohs(GETIP(q)->ip_off) > ntohs(ip->ip_off))
3971dbefcc0SGleb Smirnoff 			break;
3981dbefcc0SGleb Smirnoff 
3991dbefcc0SGleb Smirnoff 	/*
4001dbefcc0SGleb Smirnoff 	 * If there is a preceding segment, it may provide some of
4011dbefcc0SGleb Smirnoff 	 * our data already.  If so, drop the data from the incoming
4021dbefcc0SGleb Smirnoff 	 * segment.  If it provides all of our data, drop us, otherwise
4031dbefcc0SGleb Smirnoff 	 * stick new segment in the proper place.
4041dbefcc0SGleb Smirnoff 	 *
4051dbefcc0SGleb Smirnoff 	 * If some of the data is dropped from the preceding
4061dbefcc0SGleb Smirnoff 	 * segment, then it's checksum is invalidated.
4071dbefcc0SGleb Smirnoff 	 */
4081dbefcc0SGleb Smirnoff 	if (p) {
4091dbefcc0SGleb Smirnoff 		i = ntohs(GETIP(p)->ip_off) + ntohs(GETIP(p)->ip_len) -
4101dbefcc0SGleb Smirnoff 		    ntohs(ip->ip_off);
4111dbefcc0SGleb Smirnoff 		if (i > 0) {
4121dbefcc0SGleb Smirnoff 			if (i >= ntohs(ip->ip_len))
4131dbefcc0SGleb Smirnoff 				goto dropfrag;
4141dbefcc0SGleb Smirnoff 			m_adj(m, i);
4151dbefcc0SGleb Smirnoff 			m->m_pkthdr.csum_flags = 0;
4161dbefcc0SGleb Smirnoff 			ip->ip_off = htons(ntohs(ip->ip_off) + i);
4171dbefcc0SGleb Smirnoff 			ip->ip_len = htons(ntohs(ip->ip_len) - i);
4181dbefcc0SGleb Smirnoff 		}
4191dbefcc0SGleb Smirnoff 		m->m_nextpkt = p->m_nextpkt;
4201dbefcc0SGleb Smirnoff 		p->m_nextpkt = m;
4211dbefcc0SGleb Smirnoff 	} else {
4221dbefcc0SGleb Smirnoff 		m->m_nextpkt = fp->ipq_frags;
4231dbefcc0SGleb Smirnoff 		fp->ipq_frags = m;
4241dbefcc0SGleb Smirnoff 	}
4251dbefcc0SGleb Smirnoff 
4261dbefcc0SGleb Smirnoff 	/*
4271dbefcc0SGleb Smirnoff 	 * While we overlap succeeding segments trim them or,
4281dbefcc0SGleb Smirnoff 	 * if they are completely covered, dequeue them.
4291dbefcc0SGleb Smirnoff 	 */
4301dbefcc0SGleb Smirnoff 	for (; q != NULL && ntohs(ip->ip_off) + ntohs(ip->ip_len) >
4311dbefcc0SGleb Smirnoff 	    ntohs(GETIP(q)->ip_off); q = nq) {
4321dbefcc0SGleb Smirnoff 		i = (ntohs(ip->ip_off) + ntohs(ip->ip_len)) -
4331dbefcc0SGleb Smirnoff 		    ntohs(GETIP(q)->ip_off);
4341dbefcc0SGleb Smirnoff 		if (i < ntohs(GETIP(q)->ip_len)) {
4351dbefcc0SGleb Smirnoff 			GETIP(q)->ip_len = htons(ntohs(GETIP(q)->ip_len) - i);
4361dbefcc0SGleb Smirnoff 			GETIP(q)->ip_off = htons(ntohs(GETIP(q)->ip_off) + i);
4371dbefcc0SGleb Smirnoff 			m_adj(q, i);
4381dbefcc0SGleb Smirnoff 			q->m_pkthdr.csum_flags = 0;
4391dbefcc0SGleb Smirnoff 			break;
4401dbefcc0SGleb Smirnoff 		}
4411dbefcc0SGleb Smirnoff 		nq = q->m_nextpkt;
4421dbefcc0SGleb Smirnoff 		m->m_nextpkt = nq;
4431dbefcc0SGleb Smirnoff 		IPSTAT_INC(ips_fragdropped);
4441dbefcc0SGleb Smirnoff 		fp->ipq_nfrags--;
4457b9c5eb0SJonathan T. Looney 		atomic_subtract_int(&nfrags, 1);
4461dbefcc0SGleb Smirnoff 		m_freem(q);
4471dbefcc0SGleb Smirnoff 	}
4481dbefcc0SGleb Smirnoff 
4491dbefcc0SGleb Smirnoff 	/*
4501dbefcc0SGleb Smirnoff 	 * Check for complete reassembly and perform frag per packet
4511dbefcc0SGleb Smirnoff 	 * limiting.
4521dbefcc0SGleb Smirnoff 	 *
4531dbefcc0SGleb Smirnoff 	 * Frag limiting is performed here so that the nth frag has
4541dbefcc0SGleb Smirnoff 	 * a chance to complete the packet before we drop the packet.
4551dbefcc0SGleb Smirnoff 	 * As a result, n+1 frags are actually allowed per packet, but
4561dbefcc0SGleb Smirnoff 	 * only n will ever be stored. (n = maxfragsperpacket.)
4571dbefcc0SGleb Smirnoff 	 *
4581dbefcc0SGleb Smirnoff 	 */
4591dbefcc0SGleb Smirnoff 	next = 0;
4601dbefcc0SGleb Smirnoff 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
4611dbefcc0SGleb Smirnoff 		if (ntohs(GETIP(q)->ip_off) != next) {
4621dbefcc0SGleb Smirnoff 			if (fp->ipq_nfrags > V_maxfragsperpacket)
463ff790bbaSJonathan T. Looney 				ipq_drop(&V_ipq[hash], fp);
4641dbefcc0SGleb Smirnoff 			goto done;
4651dbefcc0SGleb Smirnoff 		}
4661dbefcc0SGleb Smirnoff 		next += ntohs(GETIP(q)->ip_len);
4671dbefcc0SGleb Smirnoff 	}
4681dbefcc0SGleb Smirnoff 	/* Make sure the last packet didn't have the IP_MF flag */
4691dbefcc0SGleb Smirnoff 	if (p->m_flags & M_IP_FRAG) {
4701dbefcc0SGleb Smirnoff 		if (fp->ipq_nfrags > V_maxfragsperpacket)
471ff790bbaSJonathan T. Looney 			ipq_drop(&V_ipq[hash], fp);
4721dbefcc0SGleb Smirnoff 		goto done;
4731dbefcc0SGleb Smirnoff 	}
4741dbefcc0SGleb Smirnoff 
4751dbefcc0SGleb Smirnoff 	/*
4761dbefcc0SGleb Smirnoff 	 * Reassembly is complete.  Make sure the packet is a sane size.
4771dbefcc0SGleb Smirnoff 	 */
4781dbefcc0SGleb Smirnoff 	q = fp->ipq_frags;
4791dbefcc0SGleb Smirnoff 	ip = GETIP(q);
4801dbefcc0SGleb Smirnoff 	if (next + (ip->ip_hl << 2) > IP_MAXPACKET) {
4811dbefcc0SGleb Smirnoff 		IPSTAT_INC(ips_toolong);
482ff790bbaSJonathan T. Looney 		ipq_drop(&V_ipq[hash], fp);
4831dbefcc0SGleb Smirnoff 		goto done;
4841dbefcc0SGleb Smirnoff 	}
4851dbefcc0SGleb Smirnoff 
4861dbefcc0SGleb Smirnoff 	/*
4871dbefcc0SGleb Smirnoff 	 * Concatenate fragments.
4881dbefcc0SGleb Smirnoff 	 */
4891dbefcc0SGleb Smirnoff 	m = q;
4901dbefcc0SGleb Smirnoff 	t = m->m_next;
4911dbefcc0SGleb Smirnoff 	m->m_next = NULL;
4921dbefcc0SGleb Smirnoff 	m_cat(m, t);
4931dbefcc0SGleb Smirnoff 	nq = q->m_nextpkt;
4941dbefcc0SGleb Smirnoff 	q->m_nextpkt = NULL;
4951dbefcc0SGleb Smirnoff 	for (q = nq; q != NULL; q = nq) {
4961dbefcc0SGleb Smirnoff 		nq = q->m_nextpkt;
4971dbefcc0SGleb Smirnoff 		q->m_nextpkt = NULL;
4981dbefcc0SGleb Smirnoff 		m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;
4991dbefcc0SGleb Smirnoff 		m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;
50009b0b8c0SNavdeep Parhar 		m_demote_pkthdr(q);
5011dbefcc0SGleb Smirnoff 		m_cat(m, q);
5021dbefcc0SGleb Smirnoff 	}
5031dbefcc0SGleb Smirnoff 	/*
5041dbefcc0SGleb Smirnoff 	 * In order to do checksumming faster we do 'end-around carry' here
5051dbefcc0SGleb Smirnoff 	 * (and not in for{} loop), though it implies we are not going to
5061dbefcc0SGleb Smirnoff 	 * reassemble more than 64k fragments.
5071dbefcc0SGleb Smirnoff 	 */
5081dbefcc0SGleb Smirnoff 	while (m->m_pkthdr.csum_data & 0xffff0000)
5091dbefcc0SGleb Smirnoff 		m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data & 0xffff) +
5101dbefcc0SGleb Smirnoff 		    (m->m_pkthdr.csum_data >> 16);
5117b9c5eb0SJonathan T. Looney 	atomic_subtract_int(&nfrags, fp->ipq_nfrags);
5121dbefcc0SGleb Smirnoff #ifdef MAC
5131dbefcc0SGleb Smirnoff 	mac_ipq_reassemble(fp, m);
5141dbefcc0SGleb Smirnoff 	mac_ipq_destroy(fp);
5151dbefcc0SGleb Smirnoff #endif
5161dbefcc0SGleb Smirnoff 
5171dbefcc0SGleb Smirnoff 	/*
5181dbefcc0SGleb Smirnoff 	 * Create header for new ip packet by modifying header of first
5191dbefcc0SGleb Smirnoff 	 * packet;  dequeue and discard fragment reassembly header.
5201dbefcc0SGleb Smirnoff 	 * Make header visible.
5211dbefcc0SGleb Smirnoff 	 */
5221dbefcc0SGleb Smirnoff 	ip->ip_len = htons((ip->ip_hl << 2) + next);
5231dbefcc0SGleb Smirnoff 	ip->ip_src = fp->ipq_src;
5241dbefcc0SGleb Smirnoff 	ip->ip_dst = fp->ipq_dst;
5251dbefcc0SGleb Smirnoff 	TAILQ_REMOVE(head, fp, ipq_list);
526ff790bbaSJonathan T. Looney 	V_ipq[hash].count--;
5271dbefcc0SGleb Smirnoff 	uma_zfree(V_ipq_zone, fp);
5281dbefcc0SGleb Smirnoff 	m->m_len += (ip->ip_hl << 2);
5291dbefcc0SGleb Smirnoff 	m->m_data -= (ip->ip_hl << 2);
5301dbefcc0SGleb Smirnoff 	/* some debugging cruft by sklower, below, will go away soon */
531a55383e7SHans Petter Selasky 	if (m->m_flags & M_PKTHDR) {	/* XXX this should be done elsewhere */
5321dbefcc0SGleb Smirnoff 		m_fixhdr(m);
533a55383e7SHans Petter Selasky 		/* set valid receive interface pointer */
534a55383e7SHans Petter Selasky 		m->m_pkthdr.rcvif = srcifp;
535a55383e7SHans Petter Selasky 	}
5361dbefcc0SGleb Smirnoff 	IPSTAT_INC(ips_reassembled);
537a30cb315SGleb Smirnoff 	ipreass_reschedule(&V_ipq[hash]);
5381dbefcc0SGleb Smirnoff 	IPQ_UNLOCK(hash);
5391dbefcc0SGleb Smirnoff 
5401dbefcc0SGleb Smirnoff #ifdef	RSS
5411dbefcc0SGleb Smirnoff 	/*
5421dbefcc0SGleb Smirnoff 	 * Query the RSS layer for the flowid / flowtype for the
5431dbefcc0SGleb Smirnoff 	 * mbuf payload.
5441dbefcc0SGleb Smirnoff 	 *
5451dbefcc0SGleb Smirnoff 	 * For now, just assume we have to calculate a new one.
5461dbefcc0SGleb Smirnoff 	 * Later on we should check to see if the assigned flowid matches
5471dbefcc0SGleb Smirnoff 	 * what RSS wants for the given IP protocol and if so, just keep it.
5481dbefcc0SGleb Smirnoff 	 *
5491dbefcc0SGleb Smirnoff 	 * We then queue into the relevant netisr so it can be dispatched
5501dbefcc0SGleb Smirnoff 	 * to the correct CPU.
5511dbefcc0SGleb Smirnoff 	 *
5521dbefcc0SGleb Smirnoff 	 * Note - this may return 1, which means the flowid in the mbuf
5531dbefcc0SGleb Smirnoff 	 * is correct for the configured RSS hash types and can be used.
5541dbefcc0SGleb Smirnoff 	 */
5551dbefcc0SGleb Smirnoff 	if (rss_mbuf_software_hash_v4(m, 0, &rss_hash, &rss_type) == 0) {
5561dbefcc0SGleb Smirnoff 		m->m_pkthdr.flowid = rss_hash;
5571dbefcc0SGleb Smirnoff 		M_HASHTYPE_SET(m, rss_type);
5581dbefcc0SGleb Smirnoff 	}
5591dbefcc0SGleb Smirnoff 
5601dbefcc0SGleb Smirnoff 	/*
5611dbefcc0SGleb Smirnoff 	 * Queue/dispatch for reprocessing.
5621dbefcc0SGleb Smirnoff 	 *
5631dbefcc0SGleb Smirnoff 	 * Note: this is much slower than just handling the frame in the
5641dbefcc0SGleb Smirnoff 	 * current receive context.  It's likely worth investigating
5651dbefcc0SGleb Smirnoff 	 * why this is.
5661dbefcc0SGleb Smirnoff 	 */
5671dbefcc0SGleb Smirnoff 	netisr_dispatch(NETISR_IP_DIRECT, m);
5681dbefcc0SGleb Smirnoff 	return (NULL);
5691dbefcc0SGleb Smirnoff #endif
5701dbefcc0SGleb Smirnoff 
5711dbefcc0SGleb Smirnoff 	/* Handle in-line */
5721dbefcc0SGleb Smirnoff 	return (m);
5731dbefcc0SGleb Smirnoff 
5741dbefcc0SGleb Smirnoff dropfrag:
5751dbefcc0SGleb Smirnoff 	IPSTAT_INC(ips_fragdropped);
5767b9c5eb0SJonathan T. Looney 	if (fp != NULL) {
5771dbefcc0SGleb Smirnoff 		fp->ipq_nfrags--;
5787b9c5eb0SJonathan T. Looney 		atomic_subtract_int(&nfrags, 1);
5797b9c5eb0SJonathan T. Looney 	}
5801dbefcc0SGleb Smirnoff 	m_freem(m);
5811dbefcc0SGleb Smirnoff done:
5821dbefcc0SGleb Smirnoff 	IPQ_UNLOCK(hash);
5831dbefcc0SGleb Smirnoff 	return (NULL);
5841dbefcc0SGleb Smirnoff 
5851dbefcc0SGleb Smirnoff #undef GETIP
5861dbefcc0SGleb Smirnoff }
5871dbefcc0SGleb Smirnoff 
5881dbefcc0SGleb Smirnoff /*
589a30cb315SGleb Smirnoff  * Timer expired on a bucket.
590a30cb315SGleb Smirnoff  * There should be at least one ipq to be timed out.
591160f01f0SGleb Smirnoff  */
592160f01f0SGleb Smirnoff static void
593a30cb315SGleb Smirnoff ipreass_callout(void *arg)
594160f01f0SGleb Smirnoff {
595a30cb315SGleb Smirnoff 	struct ipqbucket *bucket = arg;
596a30cb315SGleb Smirnoff 	struct ipq *fp;
597160f01f0SGleb Smirnoff 
598a30cb315SGleb Smirnoff 	IPQ_BUCKET_LOCK_ASSERT(bucket);
599a30cb315SGleb Smirnoff 	MPASS(atomic_load_int(&nfrags) > 0);
600160f01f0SGleb Smirnoff 
601a30cb315SGleb Smirnoff 	CURVNET_SET(bucket->vnet);
602a30cb315SGleb Smirnoff 	fp = TAILQ_LAST(&bucket->head, ipqhead);
60315b73a2aSGleb Smirnoff 	KASSERT(fp != NULL && fp->ipq_expire <= time_uptime,
60413018bfaSGleb Smirnoff 	    ("%s: stray callout on bucket %p, %ju < %ju", __func__, bucket,
60513018bfaSGleb Smirnoff 	    fp ? (uintmax_t)fp->ipq_expire : 0, (uintmax_t)time_uptime));
606a30cb315SGleb Smirnoff 
60715b73a2aSGleb Smirnoff 	while (fp != NULL && fp->ipq_expire <= time_uptime) {
608a30cb315SGleb Smirnoff 		ipq_timeout(bucket, fp);
609a30cb315SGleb Smirnoff 		fp = TAILQ_LAST(&bucket->head, ipqhead);
610160f01f0SGleb Smirnoff 	}
611a30cb315SGleb Smirnoff 	ipreass_reschedule(bucket);
612160f01f0SGleb Smirnoff 	CURVNET_RESTORE();
613160f01f0SGleb Smirnoff }
614160f01f0SGleb Smirnoff 
615160f01f0SGleb Smirnoff static void
616a30cb315SGleb Smirnoff ipreass_reschedule(struct ipqbucket *bucket)
617160f01f0SGleb Smirnoff {
618a30cb315SGleb Smirnoff 	struct ipq *fp;
619160f01f0SGleb Smirnoff 
620a30cb315SGleb Smirnoff 	IPQ_BUCKET_LOCK_ASSERT(bucket);
621a30cb315SGleb Smirnoff 
622a30cb315SGleb Smirnoff 	if ((fp = TAILQ_LAST(&bucket->head, ipqhead)) != NULL) {
623a30cb315SGleb Smirnoff 		time_t t;
624a30cb315SGleb Smirnoff 
625a30cb315SGleb Smirnoff 		/* Protect against time_uptime tick. */
626a30cb315SGleb Smirnoff 		t = fp->ipq_expire - time_uptime;
627a30cb315SGleb Smirnoff 		t = (t > 0) ? t : 1;
628a30cb315SGleb Smirnoff 		callout_reset_sbt(&bucket->timer, SBT_1S * t, SBT_1S,
629a30cb315SGleb Smirnoff 		    ipreass_callout, bucket, 0);
630a30cb315SGleb Smirnoff 	} else
631a30cb315SGleb Smirnoff 		callout_stop(&bucket->timer);
632160f01f0SGleb Smirnoff }
63364981536SGleb Smirnoff 
63464981536SGleb Smirnoff static void
63564981536SGleb Smirnoff ipreass_drain_vnet(void)
63664981536SGleb Smirnoff {
63729b4b63cSGleb Smirnoff 	u_int dropped = 0;
63864981536SGleb Smirnoff 
6391494f477SGleb Smirnoff 	for (int i = 0; i < V_ipq_hashsize; i++) {
64029b4b63cSGleb Smirnoff 		bool resched;
64129b4b63cSGleb Smirnoff 
64264981536SGleb Smirnoff 		IPQ_LOCK(i);
64329b4b63cSGleb Smirnoff 		resched = !TAILQ_EMPTY(&V_ipq[i].head);
64429b4b63cSGleb Smirnoff 		while(!TAILQ_EMPTY(&V_ipq[i].head)) {
64529b4b63cSGleb Smirnoff 			struct ipq *fp = TAILQ_FIRST(&V_ipq[i].head);
64629b4b63cSGleb Smirnoff 
64729b4b63cSGleb Smirnoff 			dropped += fp->ipq_nfrags;
64829b4b63cSGleb Smirnoff 			ipq_free(&V_ipq[i], fp);
64929b4b63cSGleb Smirnoff 		}
65029b4b63cSGleb Smirnoff 		if (resched)
65129b4b63cSGleb Smirnoff 			ipreass_reschedule(&V_ipq[i]);
65264981536SGleb Smirnoff 		KASSERT(V_ipq[i].count == 0,
65364981536SGleb Smirnoff 		    ("%s: V_ipq[%d] count %d (V_ipq=%p)", __func__, i,
65464981536SGleb Smirnoff 		    V_ipq[i].count, V_ipq));
65564981536SGleb Smirnoff 		IPQ_UNLOCK(i);
65664981536SGleb Smirnoff 	}
65729b4b63cSGleb Smirnoff 	IPSTAT_ADD(ips_fragdropped, dropped);
65864981536SGleb Smirnoff }
659160f01f0SGleb Smirnoff 
660160f01f0SGleb Smirnoff /*
66181a34d37SGleb Smirnoff  * Drain off all datagram fragments.
66281a34d37SGleb Smirnoff  */
66381a34d37SGleb Smirnoff static void
66481a34d37SGleb Smirnoff ipreass_drain(void)
66581a34d37SGleb Smirnoff {
66681a34d37SGleb Smirnoff 	VNET_ITERATOR_DECL(vnet_iter);
66781a34d37SGleb Smirnoff 
66881a34d37SGleb Smirnoff 	VNET_FOREACH(vnet_iter) {
66981a34d37SGleb Smirnoff 		CURVNET_SET(vnet_iter);
67064981536SGleb Smirnoff 		ipreass_drain_vnet();
67181a34d37SGleb Smirnoff 		CURVNET_RESTORE();
67281a34d37SGleb Smirnoff 	}
67381a34d37SGleb Smirnoff }
67481a34d37SGleb Smirnoff 
67581a34d37SGleb Smirnoff 
67681a34d37SGleb Smirnoff /*
6771dbefcc0SGleb Smirnoff  * Initialize IP reassembly structures.
6781dbefcc0SGleb Smirnoff  */
6791494f477SGleb Smirnoff MALLOC_DEFINE(M_IPREASS_HASH, "IP reass", "IP packet reassembly hash headers");
6801dbefcc0SGleb Smirnoff void
681aea0cd04SGleb Smirnoff ipreass_vnet_init(void)
6821dbefcc0SGleb Smirnoff {
683ff790bbaSJonathan T. Looney 	int max;
6841dbefcc0SGleb Smirnoff 
6851494f477SGleb Smirnoff 	V_ipq_hashsize = IPREASS_NHASH;
6861494f477SGleb Smirnoff 	TUNABLE_INT_FETCH("net.inet.ip.reass_hashsize", &V_ipq_hashsize);
6871494f477SGleb Smirnoff 	V_ipq = malloc(sizeof(struct ipqbucket) * V_ipq_hashsize,
6881494f477SGleb Smirnoff 	    M_IPREASS_HASH, M_WAITOK);
6891494f477SGleb Smirnoff 
6901494f477SGleb Smirnoff 	for (int i = 0; i < V_ipq_hashsize; i++) {
6911dbefcc0SGleb Smirnoff 		TAILQ_INIT(&V_ipq[i].head);
6921dbefcc0SGleb Smirnoff 		mtx_init(&V_ipq[i].lock, "IP reassembly", NULL,
6931494f477SGleb Smirnoff 		    MTX_DEF | MTX_DUPOK | MTX_NEW);
694a30cb315SGleb Smirnoff 		callout_init_mtx(&V_ipq[i].timer, &V_ipq[i].lock, 0);
695ff790bbaSJonathan T. Looney 		V_ipq[i].count = 0;
696a30cb315SGleb Smirnoff #ifdef VIMAGE
697a30cb315SGleb Smirnoff 		V_ipq[i].vnet = curvnet;
698a30cb315SGleb Smirnoff #endif
6991dbefcc0SGleb Smirnoff 	}
700c047fd1bSGleb Smirnoff 	V_ipq_hashseed = arc4random();
7011dbefcc0SGleb Smirnoff 	V_maxfragsperpacket = 16;
7021dbefcc0SGleb Smirnoff 	V_ipq_zone = uma_zcreate("ipq", sizeof(struct ipq), NULL, NULL, NULL,
7031dbefcc0SGleb Smirnoff 	    NULL, UMA_ALIGN_PTR, 0);
704a967df1cSJonathan T. Looney 	max = IP_MAXFRAGPACKETS;
705ff790bbaSJonathan T. Looney 	max = uma_zone_set_max(V_ipq_zone, max);
7061494f477SGleb Smirnoff 	V_ipreass_maxbucketsize = imax(max / (V_ipq_hashsize / 2), 1);
707aea0cd04SGleb Smirnoff }
7081dbefcc0SGleb Smirnoff 
709aea0cd04SGleb Smirnoff void
710aea0cd04SGleb Smirnoff ipreass_init(void)
711aea0cd04SGleb Smirnoff {
712aea0cd04SGleb Smirnoff 
713a967df1cSJonathan T. Looney 	maxfrags = IP_MAXFRAGS;
7141dbefcc0SGleb Smirnoff 	EVENTHANDLER_REGISTER(nmbclusters_change, ipreass_zone_change,
7151dbefcc0SGleb Smirnoff 	    NULL, EVENTHANDLER_PRI_ANY);
71681a34d37SGleb Smirnoff 	EVENTHANDLER_REGISTER(vm_lowmem, ipreass_drain, NULL,
71781a34d37SGleb Smirnoff 	    LOWMEM_PRI_DEFAULT);
71881a34d37SGleb Smirnoff 	EVENTHANDLER_REGISTER(mbuf_lowmem, ipreass_drain, NULL,
71981a34d37SGleb Smirnoff 		LOWMEM_PRI_DEFAULT);
7201dbefcc0SGleb Smirnoff }
7211dbefcc0SGleb Smirnoff 
722a55383e7SHans Petter Selasky /*
723a55383e7SHans Petter Selasky  * Drain off all datagram fragments belonging to
724a55383e7SHans Petter Selasky  * the given network interface.
725a55383e7SHans Petter Selasky  */
726a55383e7SHans Petter Selasky static void
727a55383e7SHans Petter Selasky ipreass_cleanup(void *arg __unused, struct ifnet *ifp)
728a55383e7SHans Petter Selasky {
729a55383e7SHans Petter Selasky 	struct ipq *fp, *temp;
730a55383e7SHans Petter Selasky 	struct mbuf *m;
731a55383e7SHans Petter Selasky 	int i;
732a55383e7SHans Petter Selasky 
733a55383e7SHans Petter Selasky 	KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
734a55383e7SHans Petter Selasky 
7356e6b5143SBjoern A. Zeeb 	CURVNET_SET_QUIET(ifp->if_vnet);
7366e6b5143SBjoern A. Zeeb 
737a55383e7SHans Petter Selasky 	/*
738a55383e7SHans Petter Selasky 	 * Skip processing if IPv4 reassembly is not initialised or
739a55383e7SHans Petter Selasky 	 * torn down by ipreass_destroy().
740a55383e7SHans Petter Selasky 	 */
7416e6b5143SBjoern A. Zeeb 	if (V_ipq_zone == NULL) {
7426e6b5143SBjoern A. Zeeb 		CURVNET_RESTORE();
743a55383e7SHans Petter Selasky 		return;
7446e6b5143SBjoern A. Zeeb 	}
745a55383e7SHans Petter Selasky 
7461494f477SGleb Smirnoff 	for (i = 0; i < V_ipq_hashsize; i++) {
747a55383e7SHans Petter Selasky 		IPQ_LOCK(i);
748a55383e7SHans Petter Selasky 		/* Scan fragment list. */
749a55383e7SHans Petter Selasky 		TAILQ_FOREACH_SAFE(fp, &V_ipq[i].head, ipq_list, temp) {
750a55383e7SHans Petter Selasky 			for (m = fp->ipq_frags; m != NULL; m = m->m_nextpkt) {
751a55383e7SHans Petter Selasky 				/* clear no longer valid rcvif pointer */
752a55383e7SHans Petter Selasky 				if (m->m_pkthdr.rcvif == ifp)
753a55383e7SHans Petter Selasky 					m->m_pkthdr.rcvif = NULL;
754a55383e7SHans Petter Selasky 			}
755a55383e7SHans Petter Selasky 		}
756a55383e7SHans Petter Selasky 		IPQ_UNLOCK(i);
757a55383e7SHans Petter Selasky 	}
758a55383e7SHans Petter Selasky 	CURVNET_RESTORE();
759a55383e7SHans Petter Selasky }
760a55383e7SHans Petter Selasky EVENTHANDLER_DEFINE(ifnet_departure_event, ipreass_cleanup, NULL, 0);
761a55383e7SHans Petter Selasky 
7621dbefcc0SGleb Smirnoff #ifdef VIMAGE
7631dbefcc0SGleb Smirnoff /*
7641dbefcc0SGleb Smirnoff  * Destroy IP reassembly structures.
7651dbefcc0SGleb Smirnoff  */
7661dbefcc0SGleb Smirnoff void
7671dbefcc0SGleb Smirnoff ipreass_destroy(void)
7681dbefcc0SGleb Smirnoff {
7691dbefcc0SGleb Smirnoff 
77064981536SGleb Smirnoff 	ipreass_drain_vnet();
7711dbefcc0SGleb Smirnoff 	uma_zdestroy(V_ipq_zone);
772a55383e7SHans Petter Selasky 	V_ipq_zone = NULL;
7731494f477SGleb Smirnoff 	for (int i = 0; i < V_ipq_hashsize; i++)
7741dbefcc0SGleb Smirnoff 		mtx_destroy(&V_ipq[i].lock);
775c8bc8741SGleb Smirnoff 	free(V_ipq, M_IPREASS_HASH);
7761dbefcc0SGleb Smirnoff }
7771dbefcc0SGleb Smirnoff #endif
7781dbefcc0SGleb Smirnoff 
7791dbefcc0SGleb Smirnoff /*
7801dbefcc0SGleb Smirnoff  * After maxnipq has been updated, propagate the change to UMA.  The UMA zone
7811dbefcc0SGleb Smirnoff  * max has slightly different semantics than the sysctl, for historical
7821dbefcc0SGleb Smirnoff  * reasons.
7831dbefcc0SGleb Smirnoff  */
7841dbefcc0SGleb Smirnoff static void
7851dbefcc0SGleb Smirnoff ipreass_drain_tomax(void)
7861dbefcc0SGleb Smirnoff {
787ff790bbaSJonathan T. Looney 	struct ipq *fp;
7881dbefcc0SGleb Smirnoff 	int target;
7891dbefcc0SGleb Smirnoff 
7901dbefcc0SGleb Smirnoff 	/*
791ff790bbaSJonathan T. Looney 	 * Make sure each bucket is under the new limit. If
792ff790bbaSJonathan T. Looney 	 * necessary, drop enough of the oldest elements from
793ff790bbaSJonathan T. Looney 	 * each bucket to get under the new limit.
794ff790bbaSJonathan T. Looney 	 */
7951494f477SGleb Smirnoff 	for (int i = 0; i < V_ipq_hashsize; i++) {
796ff790bbaSJonathan T. Looney 		IPQ_LOCK(i);
797ff790bbaSJonathan T. Looney 		while (V_ipq[i].count > V_ipreass_maxbucketsize &&
798ff790bbaSJonathan T. Looney 		    (fp = TAILQ_LAST(&V_ipq[i].head, ipqhead)) != NULL)
799ff790bbaSJonathan T. Looney 			ipq_timeout(&V_ipq[i], fp);
800a30cb315SGleb Smirnoff 		ipreass_reschedule(&V_ipq[i]);
801ff790bbaSJonathan T. Looney 		IPQ_UNLOCK(i);
802ff790bbaSJonathan T. Looney 	}
803ff790bbaSJonathan T. Looney 
804ff790bbaSJonathan T. Looney 	/*
8051dbefcc0SGleb Smirnoff 	 * If we are over the maximum number of fragments,
8061dbefcc0SGleb Smirnoff 	 * drain off enough to get down to the new limit,
8071dbefcc0SGleb Smirnoff 	 * stripping off last elements on queues.  Every
8081dbefcc0SGleb Smirnoff 	 * run we strip the oldest element from each bucket.
8091dbefcc0SGleb Smirnoff 	 */
8101dbefcc0SGleb Smirnoff 	target = uma_zone_get_max(V_ipq_zone);
8111dbefcc0SGleb Smirnoff 	while (uma_zone_get_cur(V_ipq_zone) > target) {
8121494f477SGleb Smirnoff 		for (int i = 0; i < V_ipq_hashsize; i++) {
8131dbefcc0SGleb Smirnoff 			IPQ_LOCK(i);
8141dbefcc0SGleb Smirnoff 			fp = TAILQ_LAST(&V_ipq[i].head, ipqhead);
815a30cb315SGleb Smirnoff 			if (fp != NULL) {
816ff790bbaSJonathan T. Looney 				ipq_timeout(&V_ipq[i], fp);
817a30cb315SGleb Smirnoff 				ipreass_reschedule(&V_ipq[i]);
818a30cb315SGleb Smirnoff 			}
8191dbefcc0SGleb Smirnoff 			IPQ_UNLOCK(i);
8201dbefcc0SGleb Smirnoff 		}
8211dbefcc0SGleb Smirnoff 	}
8221dbefcc0SGleb Smirnoff }
8231dbefcc0SGleb Smirnoff 
8241dbefcc0SGleb Smirnoff static void
8251dbefcc0SGleb Smirnoff ipreass_zone_change(void *tag)
8261dbefcc0SGleb Smirnoff {
8277b9c5eb0SJonathan T. Looney 	VNET_ITERATOR_DECL(vnet_iter);
8287b9c5eb0SJonathan T. Looney 	int max;
8291dbefcc0SGleb Smirnoff 
830a967df1cSJonathan T. Looney 	maxfrags = IP_MAXFRAGS;
831a967df1cSJonathan T. Looney 	max = IP_MAXFRAGPACKETS;
8327b9c5eb0SJonathan T. Looney 	VNET_LIST_RLOCK_NOSLEEP();
8337b9c5eb0SJonathan T. Looney 	VNET_FOREACH(vnet_iter) {
8347b9c5eb0SJonathan T. Looney 		CURVNET_SET(vnet_iter);
835ff790bbaSJonathan T. Looney 		max = uma_zone_set_max(V_ipq_zone, max);
8361494f477SGleb Smirnoff 		V_ipreass_maxbucketsize = imax(max / (V_ipq_hashsize / 2), 1);
8371dbefcc0SGleb Smirnoff 		ipreass_drain_tomax();
8387b9c5eb0SJonathan T. Looney 		CURVNET_RESTORE();
8397b9c5eb0SJonathan T. Looney 	}
8407b9c5eb0SJonathan T. Looney 	VNET_LIST_RUNLOCK_NOSLEEP();
8411dbefcc0SGleb Smirnoff }
8421dbefcc0SGleb Smirnoff 
8431dbefcc0SGleb Smirnoff /*
8441dbefcc0SGleb Smirnoff  * Change the limit on the UMA zone, or disable the fragment allocation
8451dbefcc0SGleb Smirnoff  * at all.  Since 0 and -1 is a special values here, we need our own handler,
8461dbefcc0SGleb Smirnoff  * instead of sysctl_handle_uma_zone_max().
8471dbefcc0SGleb Smirnoff  */
8481dbefcc0SGleb Smirnoff static int
8491dbefcc0SGleb Smirnoff sysctl_maxfragpackets(SYSCTL_HANDLER_ARGS)
8501dbefcc0SGleb Smirnoff {
8511dbefcc0SGleb Smirnoff 	int error, max;
8521dbefcc0SGleb Smirnoff 
8531dbefcc0SGleb Smirnoff 	if (V_noreass == 0) {
8541dbefcc0SGleb Smirnoff 		max = uma_zone_get_max(V_ipq_zone);
8551dbefcc0SGleb Smirnoff 		if (max == 0)
8561dbefcc0SGleb Smirnoff 			max = -1;
8571dbefcc0SGleb Smirnoff 	} else
8581dbefcc0SGleb Smirnoff 		max = 0;
8591dbefcc0SGleb Smirnoff 	error = sysctl_handle_int(oidp, &max, 0, req);
8601dbefcc0SGleb Smirnoff 	if (error || !req->newptr)
8611dbefcc0SGleb Smirnoff 		return (error);
8621dbefcc0SGleb Smirnoff 	if (max > 0) {
8631dbefcc0SGleb Smirnoff 		/*
8641dbefcc0SGleb Smirnoff 		 * XXXRW: Might be a good idea to sanity check the argument
8651dbefcc0SGleb Smirnoff 		 * and place an extreme upper bound.
8661dbefcc0SGleb Smirnoff 		 */
8671dbefcc0SGleb Smirnoff 		max = uma_zone_set_max(V_ipq_zone, max);
8681494f477SGleb Smirnoff 		V_ipreass_maxbucketsize = imax(max / (V_ipq_hashsize / 2), 1);
8691dbefcc0SGleb Smirnoff 		ipreass_drain_tomax();
8701dbefcc0SGleb Smirnoff 		V_noreass = 0;
8711dbefcc0SGleb Smirnoff 	} else if (max == 0) {
8721dbefcc0SGleb Smirnoff 		V_noreass = 1;
8731dbefcc0SGleb Smirnoff 		ipreass_drain();
8741dbefcc0SGleb Smirnoff 	} else if (max == -1) {
8751dbefcc0SGleb Smirnoff 		V_noreass = 0;
8761dbefcc0SGleb Smirnoff 		uma_zone_set_max(V_ipq_zone, 0);
877ff790bbaSJonathan T. Looney 		V_ipreass_maxbucketsize = INT_MAX;
8781dbefcc0SGleb Smirnoff 	} else
8791dbefcc0SGleb Smirnoff 		return (EINVAL);
8801dbefcc0SGleb Smirnoff 	return (0);
8811dbefcc0SGleb Smirnoff }
8821dbefcc0SGleb Smirnoff 
8831dbefcc0SGleb Smirnoff /*
8841dbefcc0SGleb Smirnoff  * Seek for old fragment queue header that can be reused.  Try to
8851dbefcc0SGleb Smirnoff  * reuse a header from currently locked hash bucket.
8861dbefcc0SGleb Smirnoff  */
8871dbefcc0SGleb Smirnoff static struct ipq *
8881dbefcc0SGleb Smirnoff ipq_reuse(int start)
8891dbefcc0SGleb Smirnoff {
8901dbefcc0SGleb Smirnoff 	struct ipq *fp;
891ff790bbaSJonathan T. Looney 	int bucket, i;
8921dbefcc0SGleb Smirnoff 
8931dbefcc0SGleb Smirnoff 	IPQ_LOCK_ASSERT(start);
8941dbefcc0SGleb Smirnoff 
8951494f477SGleb Smirnoff 	for (i = 0; i < V_ipq_hashsize; i++) {
8961494f477SGleb Smirnoff 		bucket = (start + i) % V_ipq_hashsize;
897ff790bbaSJonathan T. Looney 		if (bucket != start && IPQ_TRYLOCK(bucket) == 0)
8981dbefcc0SGleb Smirnoff 			continue;
899ff790bbaSJonathan T. Looney 		fp = TAILQ_LAST(&V_ipq[bucket].head, ipqhead);
9001dbefcc0SGleb Smirnoff 		if (fp) {
9011dbefcc0SGleb Smirnoff 			struct mbuf *m;
9021dbefcc0SGleb Smirnoff 
9031dbefcc0SGleb Smirnoff 			IPSTAT_ADD(ips_fragtimeout, fp->ipq_nfrags);
9047b9c5eb0SJonathan T. Looney 			atomic_subtract_int(&nfrags, fp->ipq_nfrags);
9051dbefcc0SGleb Smirnoff 			while (fp->ipq_frags) {
9061dbefcc0SGleb Smirnoff 				m = fp->ipq_frags;
9071dbefcc0SGleb Smirnoff 				fp->ipq_frags = m->m_nextpkt;
9081dbefcc0SGleb Smirnoff 				m_freem(m);
9091dbefcc0SGleb Smirnoff 			}
910ff790bbaSJonathan T. Looney 			TAILQ_REMOVE(&V_ipq[bucket].head, fp, ipq_list);
911ff790bbaSJonathan T. Looney 			V_ipq[bucket].count--;
912a30cb315SGleb Smirnoff 			ipreass_reschedule(&V_ipq[bucket]);
913ff790bbaSJonathan T. Looney 			if (bucket != start)
914ff790bbaSJonathan T. Looney 				IPQ_UNLOCK(bucket);
915ff790bbaSJonathan T. Looney 			break;
916ff790bbaSJonathan T. Looney 		}
917ff790bbaSJonathan T. Looney 		if (bucket != start)
918ff790bbaSJonathan T. Looney 			IPQ_UNLOCK(bucket);
919ff790bbaSJonathan T. Looney 	}
9201dbefcc0SGleb Smirnoff 	IPQ_LOCK_ASSERT(start);
9211dbefcc0SGleb Smirnoff 	return (fp);
9221dbefcc0SGleb Smirnoff }
9231dbefcc0SGleb Smirnoff 
9241dbefcc0SGleb Smirnoff /*
9251dbefcc0SGleb Smirnoff  * Free a fragment reassembly header and all associated datagrams.
9261dbefcc0SGleb Smirnoff  */
9271dbefcc0SGleb Smirnoff static void
928ff790bbaSJonathan T. Looney ipq_free(struct ipqbucket *bucket, struct ipq *fp)
9291dbefcc0SGleb Smirnoff {
9301dbefcc0SGleb Smirnoff 	struct mbuf *q;
9311dbefcc0SGleb Smirnoff 
9327b9c5eb0SJonathan T. Looney 	atomic_subtract_int(&nfrags, fp->ipq_nfrags);
9331dbefcc0SGleb Smirnoff 	while (fp->ipq_frags) {
9341dbefcc0SGleb Smirnoff 		q = fp->ipq_frags;
9351dbefcc0SGleb Smirnoff 		fp->ipq_frags = q->m_nextpkt;
9361dbefcc0SGleb Smirnoff 		m_freem(q);
9371dbefcc0SGleb Smirnoff 	}
938ff790bbaSJonathan T. Looney 	TAILQ_REMOVE(&bucket->head, fp, ipq_list);
939ff790bbaSJonathan T. Looney 	bucket->count--;
9401dbefcc0SGleb Smirnoff 	uma_zfree(V_ipq_zone, fp);
9411dbefcc0SGleb Smirnoff }
942ff790bbaSJonathan T. Looney 
943ff790bbaSJonathan T. Looney /*
944ff790bbaSJonathan T. Looney  * Get or set the maximum number of reassembly queues per bucket.
945ff790bbaSJonathan T. Looney  */
946ff790bbaSJonathan T. Looney static int
947ff790bbaSJonathan T. Looney sysctl_maxfragbucketsize(SYSCTL_HANDLER_ARGS)
948ff790bbaSJonathan T. Looney {
949ff790bbaSJonathan T. Looney 	int error, max;
950ff790bbaSJonathan T. Looney 
951ff790bbaSJonathan T. Looney 	max = V_ipreass_maxbucketsize;
952ff790bbaSJonathan T. Looney 	error = sysctl_handle_int(oidp, &max, 0, req);
953ff790bbaSJonathan T. Looney 	if (error || !req->newptr)
954ff790bbaSJonathan T. Looney 		return (error);
955ff790bbaSJonathan T. Looney 	if (max <= 0)
956ff790bbaSJonathan T. Looney 		return (EINVAL);
957ff790bbaSJonathan T. Looney 	V_ipreass_maxbucketsize = max;
958ff790bbaSJonathan T. Looney 	ipreass_drain_tomax();
959ff790bbaSJonathan T. Looney 	return (0);
960ff790bbaSJonathan T. Looney }
961a30cb315SGleb Smirnoff 
962a30cb315SGleb Smirnoff /*
963a30cb315SGleb Smirnoff  * Get or set the IP fragment time to live.
964a30cb315SGleb Smirnoff  */
965a30cb315SGleb Smirnoff static int
966a30cb315SGleb Smirnoff sysctl_fragttl(SYSCTL_HANDLER_ARGS)
967a30cb315SGleb Smirnoff {
968a30cb315SGleb Smirnoff 	u_int ttl;
969a30cb315SGleb Smirnoff 	int error;
970a30cb315SGleb Smirnoff 
971a30cb315SGleb Smirnoff 	ttl = V_ipfragttl;
972a30cb315SGleb Smirnoff 	error = sysctl_handle_int(oidp, &ttl, 0, req);
973a30cb315SGleb Smirnoff 	if (error || !req->newptr)
974a30cb315SGleb Smirnoff 		return (error);
975a30cb315SGleb Smirnoff 
976a30cb315SGleb Smirnoff 	if (ttl < 1 || ttl > MAXTTL)
977a30cb315SGleb Smirnoff 		return (EINVAL);
978a30cb315SGleb Smirnoff 
979a30cb315SGleb Smirnoff 	atomic_store_int(&V_ipfragttl, ttl);
980a30cb315SGleb Smirnoff 	return (0);
981a30cb315SGleb Smirnoff }
982