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
321dbefcc0SGleb Smirnoff #include <sys/cdefs.h>
331dbefcc0SGleb Smirnoff #include "opt_rss.h"
341dbefcc0SGleb Smirnoff
351dbefcc0SGleb Smirnoff #include <sys/param.h>
361dbefcc0SGleb Smirnoff #include <sys/systm.h>
371dbefcc0SGleb Smirnoff #include <sys/eventhandler.h>
3804f44499SHans Petter Selasky #include <sys/kernel.h>
39c047fd1bSGleb Smirnoff #include <sys/hash.h>
401dbefcc0SGleb Smirnoff #include <sys/mbuf.h>
411dbefcc0SGleb Smirnoff #include <sys/malloc.h>
42ff790bbaSJonathan T. Looney #include <sys/limits.h>
431dbefcc0SGleb Smirnoff #include <sys/lock.h>
441dbefcc0SGleb Smirnoff #include <sys/mutex.h>
451dbefcc0SGleb Smirnoff #include <sys/sysctl.h>
46a55383e7SHans Petter Selasky #include <sys/socket.h>
471dbefcc0SGleb Smirnoff
48a55383e7SHans Petter Selasky #include <net/if.h>
49a55383e7SHans Petter Selasky #include <net/if_var.h>
503d0d5b21SJustin Hibbits #include <net/if_private.h>
511dbefcc0SGleb Smirnoff #include <net/rss_config.h>
523e217461SAdrian Chadd #include <net/netisr.h>
531dbefcc0SGleb Smirnoff #include <net/vnet.h>
541dbefcc0SGleb Smirnoff
551dbefcc0SGleb Smirnoff #include <netinet/in.h>
561dbefcc0SGleb Smirnoff #include <netinet/ip.h>
571dbefcc0SGleb Smirnoff #include <netinet/ip_var.h>
581dbefcc0SGleb Smirnoff #include <netinet/in_rss.h>
591dbefcc0SGleb Smirnoff #ifdef MAC
601dbefcc0SGleb Smirnoff #include <security/mac/mac_framework.h>
611dbefcc0SGleb Smirnoff #endif
621dbefcc0SGleb Smirnoff
631dbefcc0SGleb Smirnoff SYSCTL_DECL(_net_inet_ip);
641dbefcc0SGleb Smirnoff
651dbefcc0SGleb Smirnoff /*
661dbefcc0SGleb Smirnoff * Reassembly headers are stored in hash buckets.
671dbefcc0SGleb Smirnoff */
68a967df1cSJonathan T. Looney #define IPREASS_NHASH_LOG2 10
691dbefcc0SGleb Smirnoff #define IPREASS_NHASH (1 << IPREASS_NHASH_LOG2)
70c8bc8741SGleb Smirnoff #define IPREASS_HMASK (V_ipq_hashsize - 1)
711dbefcc0SGleb Smirnoff
721dbefcc0SGleb Smirnoff struct ipqbucket {
731dbefcc0SGleb Smirnoff TAILQ_HEAD(ipqhead, ipq) head;
741dbefcc0SGleb Smirnoff struct mtx lock;
75a30cb315SGleb Smirnoff struct callout timer;
76a30cb315SGleb Smirnoff #ifdef VIMAGE
77a30cb315SGleb Smirnoff struct vnet *vnet;
78a30cb315SGleb Smirnoff #endif
79ff790bbaSJonathan T. Looney int count;
801dbefcc0SGleb Smirnoff };
811dbefcc0SGleb Smirnoff
821494f477SGleb Smirnoff VNET_DEFINE_STATIC(struct ipqbucket *, ipq);
831dbefcc0SGleb Smirnoff #define V_ipq VNET(ipq)
845f901c92SAndrew Turner VNET_DEFINE_STATIC(uint32_t, ipq_hashseed);
85c047fd1bSGleb Smirnoff #define V_ipq_hashseed VNET(ipq_hashseed)
861494f477SGleb Smirnoff VNET_DEFINE_STATIC(uint32_t, ipq_hashsize);
871494f477SGleb Smirnoff #define V_ipq_hashsize VNET(ipq_hashsize)
881dbefcc0SGleb Smirnoff
891dbefcc0SGleb Smirnoff #define IPQ_LOCK(i) mtx_lock(&V_ipq[i].lock)
901dbefcc0SGleb Smirnoff #define IPQ_TRYLOCK(i) mtx_trylock(&V_ipq[i].lock)
911dbefcc0SGleb Smirnoff #define IPQ_UNLOCK(i) mtx_unlock(&V_ipq[i].lock)
921dbefcc0SGleb Smirnoff #define IPQ_LOCK_ASSERT(i) mtx_assert(&V_ipq[i].lock, MA_OWNED)
93a30cb315SGleb Smirnoff #define IPQ_BUCKET_LOCK_ASSERT(b) mtx_assert(&(b)->lock, MA_OWNED)
941dbefcc0SGleb Smirnoff
95ff790bbaSJonathan T. Looney VNET_DEFINE_STATIC(int, ipreass_maxbucketsize);
96ff790bbaSJonathan T. Looney #define V_ipreass_maxbucketsize VNET(ipreass_maxbucketsize)
97ff790bbaSJonathan T. Looney
981dbefcc0SGleb Smirnoff void ipreass_init(void);
99aea0cd04SGleb Smirnoff void ipreass_vnet_init(void);
1001dbefcc0SGleb Smirnoff #ifdef VIMAGE
1011dbefcc0SGleb Smirnoff void ipreass_destroy(void);
1021dbefcc0SGleb Smirnoff #endif
1031dbefcc0SGleb Smirnoff static int sysctl_maxfragpackets(SYSCTL_HANDLER_ARGS);
104ff790bbaSJonathan T. Looney static int sysctl_maxfragbucketsize(SYSCTL_HANDLER_ARGS);
105a30cb315SGleb Smirnoff static int sysctl_fragttl(SYSCTL_HANDLER_ARGS);
1061dbefcc0SGleb Smirnoff static void ipreass_zone_change(void *);
1071dbefcc0SGleb Smirnoff static void ipreass_drain_tomax(void);
108ff790bbaSJonathan T. Looney static void ipq_free(struct ipqbucket *, struct ipq *);
1091dbefcc0SGleb Smirnoff static struct ipq * ipq_reuse(int);
110a30cb315SGleb Smirnoff static void ipreass_callout(void *);
111a30cb315SGleb Smirnoff static void ipreass_reschedule(struct ipqbucket *);
1121dbefcc0SGleb Smirnoff
1131dbefcc0SGleb Smirnoff static inline void
ipq_timeout(struct ipqbucket * bucket,struct ipq * fp)114ff790bbaSJonathan T. Looney ipq_timeout(struct ipqbucket *bucket, struct ipq *fp)
1151dbefcc0SGleb Smirnoff {
1161dbefcc0SGleb Smirnoff
1171dbefcc0SGleb Smirnoff IPSTAT_ADD(ips_fragtimeout, fp->ipq_nfrags);
118ff790bbaSJonathan T. Looney ipq_free(bucket, fp);
1191dbefcc0SGleb Smirnoff }
1201dbefcc0SGleb Smirnoff
1211dbefcc0SGleb Smirnoff static inline void
ipq_drop(struct ipqbucket * bucket,struct ipq * fp)122ff790bbaSJonathan T. Looney ipq_drop(struct ipqbucket *bucket, struct ipq *fp)
1231dbefcc0SGleb Smirnoff {
1241dbefcc0SGleb Smirnoff
1251dbefcc0SGleb Smirnoff IPSTAT_ADD(ips_fragdropped, fp->ipq_nfrags);
126ff790bbaSJonathan T. Looney ipq_free(bucket, fp);
127a30cb315SGleb Smirnoff ipreass_reschedule(bucket);
1281dbefcc0SGleb Smirnoff }
1291dbefcc0SGleb Smirnoff
130a967df1cSJonathan T. Looney /*
131a967df1cSJonathan T. Looney * By default, limit the number of IP fragments across all reassembly
132a967df1cSJonathan T. Looney * queues to 1/32 of the total number of mbuf clusters.
133a967df1cSJonathan T. Looney *
134a967df1cSJonathan T. Looney * Limit the total number of reassembly queues per VNET to the
135a967df1cSJonathan T. Looney * IP fragment limit, but ensure the limit will not allow any bucket
136a967df1cSJonathan T. Looney * to grow above 100 items. (The bucket limit is
1371494f477SGleb Smirnoff * IP_MAXFRAGPACKETS / (V_ipq_hashsize / 2), so the 50 is the correct
138a967df1cSJonathan T. Looney * multiplier to reach a 100-item limit.)
139a967df1cSJonathan T. Looney * The 100-item limit was chosen as brief testing seems to show that
140a967df1cSJonathan T. Looney * this produces "reasonable" performance on some subset of systems
141a967df1cSJonathan T. Looney * under DoS attack.
142a967df1cSJonathan T. Looney */
143a967df1cSJonathan T. Looney #define IP_MAXFRAGS (nmbclusters / 32)
1441494f477SGleb Smirnoff #define IP_MAXFRAGPACKETS (imin(IP_MAXFRAGS, V_ipq_hashsize * 50))
145a967df1cSJonathan T. Looney
1467b9c5eb0SJonathan T. Looney static int maxfrags;
147d2b95af1SMateusz Guzik static u_int __exclusive_cache_line nfrags;
1487b9c5eb0SJonathan T. Looney SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfrags, CTLFLAG_RW,
1497b9c5eb0SJonathan T. Looney &maxfrags, 0,
1507b9c5eb0SJonathan T. Looney "Maximum number of IPv4 fragments allowed across all reassembly queues");
1517b9c5eb0SJonathan T. Looney SYSCTL_UINT(_net_inet_ip, OID_AUTO, curfrags, CTLFLAG_RD,
152d2b95af1SMateusz Guzik &nfrags, 0,
1537b9c5eb0SJonathan T. Looney "Current number of IPv4 fragments across all reassembly queues");
1547b9c5eb0SJonathan T. Looney
1555f901c92SAndrew Turner VNET_DEFINE_STATIC(uma_zone_t, ipq_zone);
1561dbefcc0SGleb Smirnoff #define V_ipq_zone VNET(ipq_zone)
1571494f477SGleb Smirnoff
1581494f477SGleb Smirnoff SYSCTL_UINT(_net_inet_ip, OID_AUTO, reass_hashsize,
1591494f477SGleb Smirnoff CTLFLAG_VNET | CTLFLAG_RDTUN, &VNET_NAME(ipq_hashsize), 0,
1601494f477SGleb Smirnoff "Size of IP fragment reassembly hashtable");
1611494f477SGleb Smirnoff
1627029da5cSPawel Biernacki SYSCTL_PROC(_net_inet_ip, OID_AUTO, maxfragpackets,
1637029da5cSPawel Biernacki CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
1647029da5cSPawel Biernacki NULL, 0, sysctl_maxfragpackets, "I",
1651dbefcc0SGleb Smirnoff "Maximum number of IPv4 fragment reassembly queue entries");
1661dbefcc0SGleb Smirnoff SYSCTL_UMA_CUR(_net_inet_ip, OID_AUTO, fragpackets, CTLFLAG_VNET,
1671dbefcc0SGleb Smirnoff &VNET_NAME(ipq_zone),
1681dbefcc0SGleb Smirnoff "Current number of IPv4 fragment reassembly queue entries");
1691dbefcc0SGleb Smirnoff
1705f901c92SAndrew Turner VNET_DEFINE_STATIC(int, noreass);
1711dbefcc0SGleb Smirnoff #define V_noreass VNET(noreass)
1721dbefcc0SGleb Smirnoff
1735f901c92SAndrew Turner VNET_DEFINE_STATIC(int, maxfragsperpacket);
1741dbefcc0SGleb Smirnoff #define V_maxfragsperpacket VNET(maxfragsperpacket)
1751dbefcc0SGleb Smirnoff SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfragsperpacket, CTLFLAG_VNET | CTLFLAG_RW,
1761dbefcc0SGleb Smirnoff &VNET_NAME(maxfragsperpacket), 0,
1771dbefcc0SGleb Smirnoff "Maximum number of IPv4 fragments allowed per packet");
178ff790bbaSJonathan T. Looney SYSCTL_PROC(_net_inet_ip, OID_AUTO, maxfragbucketsize,
179ff790bbaSJonathan T. Looney CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, NULL, 0,
180ff790bbaSJonathan T. Looney sysctl_maxfragbucketsize, "I",
181ff790bbaSJonathan T. Looney "Maximum number of IPv4 fragment reassembly queue entries per bucket");
1821dbefcc0SGleb Smirnoff
183a30cb315SGleb Smirnoff VNET_DEFINE_STATIC(u_int, ipfragttl) = 30;
184a30cb315SGleb Smirnoff #define V_ipfragttl VNET(ipfragttl)
185a30cb315SGleb Smirnoff SYSCTL_PROC(_net_inet_ip, OID_AUTO, fragttl, CTLTYPE_INT | CTLFLAG_RW |
186a30cb315SGleb Smirnoff CTLFLAG_MPSAFE | CTLFLAG_VNET, NULL, 0, sysctl_fragttl, "IU",
187a30cb315SGleb Smirnoff "IP fragment life time on reassembly queue (seconds)");
1888338690aSGleb Smirnoff
1891dbefcc0SGleb Smirnoff /*
1901dbefcc0SGleb Smirnoff * Take incoming datagram fragment and try to reassemble it into
1911dbefcc0SGleb Smirnoff * whole datagram. If the argument is the first fragment or one
1921dbefcc0SGleb Smirnoff * in between the function will return NULL and store the mbuf
1931dbefcc0SGleb Smirnoff * in the fragment chain. If the argument is the last fragment
1941dbefcc0SGleb Smirnoff * the packet will be reassembled and the pointer to the new
1951dbefcc0SGleb Smirnoff * mbuf returned for further processing. Only m_tags attached
1961dbefcc0SGleb Smirnoff * to the first packet/fragment are preserved.
1971dbefcc0SGleb Smirnoff * The IP header is *NOT* adjusted out of iplen.
1981dbefcc0SGleb Smirnoff */
1991dbefcc0SGleb Smirnoff #define M_IP_FRAG M_PROTO9
2001dbefcc0SGleb Smirnoff struct mbuf *
ip_reass(struct mbuf * m)2011dbefcc0SGleb Smirnoff ip_reass(struct mbuf *m)
2021dbefcc0SGleb Smirnoff {
2031dbefcc0SGleb Smirnoff struct ip *ip;
2041dbefcc0SGleb Smirnoff struct mbuf *p, *q, *nq, *t;
2051dbefcc0SGleb Smirnoff struct ipq *fp;
206a55383e7SHans Petter Selasky struct ifnet *srcifp;
2071dbefcc0SGleb Smirnoff struct ipqhead *head;
2087b9c5eb0SJonathan T. Looney int i, hlen, next, tmpmax;
2091dbefcc0SGleb Smirnoff u_int8_t ecn, ecn0;
2105d9bd455SJonathan T. Looney uint32_t hash, hashkey[3];
2111dbefcc0SGleb Smirnoff #ifdef RSS
2121dbefcc0SGleb Smirnoff uint32_t rss_hash, rss_type;
2131dbefcc0SGleb Smirnoff #endif
2141dbefcc0SGleb Smirnoff
2151dbefcc0SGleb Smirnoff /*
2161dbefcc0SGleb Smirnoff * If no reassembling or maxfragsperpacket are 0,
2171dbefcc0SGleb Smirnoff * never accept fragments.
2187b9c5eb0SJonathan T. Looney * Also, drop packet if it would exceed the maximum
2197b9c5eb0SJonathan T. Looney * number of fragments.
2201dbefcc0SGleb Smirnoff */
2217b9c5eb0SJonathan T. Looney tmpmax = maxfrags;
2227b9c5eb0SJonathan T. Looney if (V_noreass == 1 || V_maxfragsperpacket == 0 ||
2237b9c5eb0SJonathan T. Looney (tmpmax >= 0 && atomic_load_int(&nfrags) >= (u_int)tmpmax)) {
2241dbefcc0SGleb Smirnoff IPSTAT_INC(ips_fragments);
2251dbefcc0SGleb Smirnoff IPSTAT_INC(ips_fragdropped);
2261dbefcc0SGleb Smirnoff m_freem(m);
2271dbefcc0SGleb Smirnoff return (NULL);
2281dbefcc0SGleb Smirnoff }
2291dbefcc0SGleb Smirnoff
2301dbefcc0SGleb Smirnoff ip = mtod(m, struct ip *);
2311dbefcc0SGleb Smirnoff hlen = ip->ip_hl << 2;
2321dbefcc0SGleb Smirnoff
2331dbefcc0SGleb Smirnoff /*
2341dbefcc0SGleb Smirnoff * Adjust ip_len to not reflect header,
2351dbefcc0SGleb Smirnoff * convert offset of this to bytes.
2361dbefcc0SGleb Smirnoff */
2371dbefcc0SGleb Smirnoff ip->ip_len = htons(ntohs(ip->ip_len) - hlen);
2381dbefcc0SGleb Smirnoff /*
2391dbefcc0SGleb Smirnoff * Make sure that fragments have a data length
2402157f3c3SJonathan T. Looney * that's a non-zero multiple of 8 bytes, unless
2412157f3c3SJonathan T. Looney * this is the last fragment.
2421dbefcc0SGleb Smirnoff */
2432157f3c3SJonathan T. Looney if (ip->ip_len == htons(0) ||
2442157f3c3SJonathan T. Looney ((ip->ip_off & htons(IP_MF)) && (ntohs(ip->ip_len) & 0x7) != 0)) {
2451dbefcc0SGleb Smirnoff IPSTAT_INC(ips_toosmall); /* XXX */
2461dbefcc0SGleb Smirnoff IPSTAT_INC(ips_fragdropped);
2471dbefcc0SGleb Smirnoff m_freem(m);
2481dbefcc0SGleb Smirnoff return (NULL);
2491dbefcc0SGleb Smirnoff }
2502157f3c3SJonathan T. Looney if (ip->ip_off & htons(IP_MF))
2511dbefcc0SGleb Smirnoff m->m_flags |= M_IP_FRAG;
2522157f3c3SJonathan T. Looney else
2531dbefcc0SGleb Smirnoff m->m_flags &= ~M_IP_FRAG;
2541dbefcc0SGleb Smirnoff ip->ip_off = htons(ntohs(ip->ip_off) << 3);
2551dbefcc0SGleb Smirnoff
2561dbefcc0SGleb Smirnoff /*
25786af1d02SMark Johnston * Make sure the fragment lies within a packet of valid size.
25886af1d02SMark Johnston */
25986af1d02SMark Johnston if (ntohs(ip->ip_len) + ntohs(ip->ip_off) > IP_MAXPACKET) {
26086af1d02SMark Johnston IPSTAT_INC(ips_toolong);
26186af1d02SMark Johnston IPSTAT_INC(ips_fragdropped);
26286af1d02SMark Johnston m_freem(m);
26386af1d02SMark Johnston return (NULL);
26486af1d02SMark Johnston }
26586af1d02SMark Johnston
26686af1d02SMark Johnston /*
267a55383e7SHans Petter Selasky * Store receive network interface pointer for later.
268a55383e7SHans Petter Selasky */
269a55383e7SHans Petter Selasky srcifp = m->m_pkthdr.rcvif;
270a55383e7SHans Petter Selasky
271a55383e7SHans Petter Selasky /*
2721dbefcc0SGleb Smirnoff * Attempt reassembly; if it succeeds, proceed.
2731dbefcc0SGleb Smirnoff * ip_reass() will return a different mbuf.
2741dbefcc0SGleb Smirnoff */
2751dbefcc0SGleb Smirnoff IPSTAT_INC(ips_fragments);
2761dbefcc0SGleb Smirnoff m->m_pkthdr.PH_loc.ptr = ip;
2771dbefcc0SGleb Smirnoff
2781dbefcc0SGleb Smirnoff /*
2791dbefcc0SGleb Smirnoff * Presence of header sizes in mbufs
2801dbefcc0SGleb Smirnoff * would confuse code below.
2811dbefcc0SGleb Smirnoff */
2821dbefcc0SGleb Smirnoff m->m_data += hlen;
2831dbefcc0SGleb Smirnoff m->m_len -= hlen;
2841dbefcc0SGleb Smirnoff
2855d9bd455SJonathan T. Looney hashkey[0] = ip->ip_src.s_addr;
2865d9bd455SJonathan T. Looney hashkey[1] = ip->ip_dst.s_addr;
2875d9bd455SJonathan T. Looney hashkey[2] = (uint32_t)ip->ip_p << 16;
2885d9bd455SJonathan T. Looney hashkey[2] += ip->ip_id;
2895d9bd455SJonathan T. Looney hash = jenkins_hash32(hashkey, nitems(hashkey), V_ipq_hashseed);
2905d9bd455SJonathan T. Looney hash &= IPREASS_HMASK;
2911dbefcc0SGleb Smirnoff head = &V_ipq[hash].head;
2921dbefcc0SGleb Smirnoff IPQ_LOCK(hash);
2931dbefcc0SGleb Smirnoff
2941dbefcc0SGleb Smirnoff /*
2951dbefcc0SGleb Smirnoff * Look for queue of fragments
2961dbefcc0SGleb Smirnoff * of this datagram.
2971dbefcc0SGleb Smirnoff */
2981dbefcc0SGleb Smirnoff TAILQ_FOREACH(fp, head, ipq_list)
2991dbefcc0SGleb Smirnoff if (ip->ip_id == fp->ipq_id &&
3001dbefcc0SGleb Smirnoff ip->ip_src.s_addr == fp->ipq_src.s_addr &&
3011dbefcc0SGleb Smirnoff ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
3021dbefcc0SGleb Smirnoff #ifdef MAC
3031dbefcc0SGleb Smirnoff mac_ipq_match(m, fp) &&
3041dbefcc0SGleb Smirnoff #endif
3051dbefcc0SGleb Smirnoff ip->ip_p == fp->ipq_p)
3061dbefcc0SGleb Smirnoff break;
3071dbefcc0SGleb Smirnoff /*
3081dbefcc0SGleb Smirnoff * If first fragment to arrive, create a reassembly queue.
3091dbefcc0SGleb Smirnoff */
3101dbefcc0SGleb Smirnoff if (fp == NULL) {
311ff790bbaSJonathan T. Looney if (V_ipq[hash].count < V_ipreass_maxbucketsize)
3121dbefcc0SGleb Smirnoff fp = uma_zalloc(V_ipq_zone, M_NOWAIT);
3131dbefcc0SGleb Smirnoff if (fp == NULL)
3141dbefcc0SGleb Smirnoff fp = ipq_reuse(hash);
315ff790bbaSJonathan T. Looney if (fp == NULL)
316ff790bbaSJonathan T. Looney goto dropfrag;
3171dbefcc0SGleb Smirnoff #ifdef MAC
3181dbefcc0SGleb Smirnoff if (mac_ipq_init(fp, M_NOWAIT) != 0) {
3191dbefcc0SGleb Smirnoff uma_zfree(V_ipq_zone, fp);
3201dbefcc0SGleb Smirnoff fp = NULL;
3211dbefcc0SGleb Smirnoff goto dropfrag;
3221dbefcc0SGleb Smirnoff }
3231dbefcc0SGleb Smirnoff mac_ipq_create(m, fp);
3241dbefcc0SGleb Smirnoff #endif
3251dbefcc0SGleb Smirnoff TAILQ_INSERT_HEAD(head, fp, ipq_list);
326ff790bbaSJonathan T. Looney V_ipq[hash].count++;
3271dbefcc0SGleb Smirnoff fp->ipq_nfrags = 1;
3287b9c5eb0SJonathan T. Looney atomic_add_int(&nfrags, 1);
329a30cb315SGleb Smirnoff fp->ipq_expire = time_uptime + V_ipfragttl;
3301dbefcc0SGleb Smirnoff fp->ipq_p = ip->ip_p;
3311dbefcc0SGleb Smirnoff fp->ipq_id = ip->ip_id;
3321dbefcc0SGleb Smirnoff fp->ipq_src = ip->ip_src;
3331dbefcc0SGleb Smirnoff fp->ipq_dst = ip->ip_dst;
3341dbefcc0SGleb Smirnoff fp->ipq_frags = m;
3352157f3c3SJonathan T. Looney if (m->m_flags & M_IP_FRAG)
3362157f3c3SJonathan T. Looney fp->ipq_maxoff = -1;
3372157f3c3SJonathan T. Looney else
3382157f3c3SJonathan T. Looney fp->ipq_maxoff = ntohs(ip->ip_off) + ntohs(ip->ip_len);
3391dbefcc0SGleb Smirnoff m->m_nextpkt = NULL;
340a30cb315SGleb Smirnoff if (fp == TAILQ_LAST(head, ipqhead))
341a30cb315SGleb Smirnoff callout_reset_sbt(&V_ipq[hash].timer,
342a30cb315SGleb Smirnoff SBT_1S * V_ipfragttl, SBT_1S, ipreass_callout,
343a30cb315SGleb Smirnoff &V_ipq[hash], 0);
344a30cb315SGleb Smirnoff else
345a30cb315SGleb Smirnoff MPASS(callout_active(&V_ipq[hash].timer));
3461dbefcc0SGleb Smirnoff goto done;
3471dbefcc0SGleb Smirnoff } else {
3482157f3c3SJonathan T. Looney /*
3492157f3c3SJonathan T. Looney * If we already saw the last fragment, make sure
3502157f3c3SJonathan T. Looney * this fragment's offset looks sane. Otherwise, if
3512157f3c3SJonathan T. Looney * this is the last fragment, record its endpoint.
3522157f3c3SJonathan T. Looney */
3532157f3c3SJonathan T. Looney if (fp->ipq_maxoff > 0) {
3542157f3c3SJonathan T. Looney i = ntohs(ip->ip_off) + ntohs(ip->ip_len);
3552157f3c3SJonathan T. Looney if (((m->m_flags & M_IP_FRAG) && i >= fp->ipq_maxoff) ||
3562157f3c3SJonathan T. Looney ((m->m_flags & M_IP_FRAG) == 0 &&
3572157f3c3SJonathan T. Looney i != fp->ipq_maxoff)) {
3582157f3c3SJonathan T. Looney fp = NULL;
3592157f3c3SJonathan T. Looney goto dropfrag;
3602157f3c3SJonathan T. Looney }
3612157f3c3SJonathan T. Looney } else if ((m->m_flags & M_IP_FRAG) == 0)
3622157f3c3SJonathan T. Looney fp->ipq_maxoff = ntohs(ip->ip_off) + ntohs(ip->ip_len);
3631dbefcc0SGleb Smirnoff fp->ipq_nfrags++;
3647b9c5eb0SJonathan T. Looney atomic_add_int(&nfrags, 1);
3651dbefcc0SGleb Smirnoff #ifdef MAC
3661dbefcc0SGleb Smirnoff mac_ipq_update(m, fp);
3671dbefcc0SGleb Smirnoff #endif
3681dbefcc0SGleb Smirnoff }
3691dbefcc0SGleb Smirnoff
3701dbefcc0SGleb Smirnoff #define GETIP(m) ((struct ip*)((m)->m_pkthdr.PH_loc.ptr))
3711dbefcc0SGleb Smirnoff
3721dbefcc0SGleb Smirnoff /*
3731dbefcc0SGleb Smirnoff * Handle ECN by comparing this segment with the first one;
3741dbefcc0SGleb Smirnoff * if CE is set, do not lose CE.
3751dbefcc0SGleb Smirnoff * drop if CE and not-ECT are mixed for the same packet.
3761dbefcc0SGleb Smirnoff */
3771dbefcc0SGleb Smirnoff ecn = ip->ip_tos & IPTOS_ECN_MASK;
3781dbefcc0SGleb Smirnoff ecn0 = GETIP(fp->ipq_frags)->ip_tos & IPTOS_ECN_MASK;
3791dbefcc0SGleb Smirnoff if (ecn == IPTOS_ECN_CE) {
3801dbefcc0SGleb Smirnoff if (ecn0 == IPTOS_ECN_NOTECT)
3811dbefcc0SGleb Smirnoff goto dropfrag;
3821dbefcc0SGleb Smirnoff if (ecn0 != IPTOS_ECN_CE)
3831dbefcc0SGleb Smirnoff GETIP(fp->ipq_frags)->ip_tos |= IPTOS_ECN_CE;
3841dbefcc0SGleb Smirnoff }
3851dbefcc0SGleb Smirnoff if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT)
3861dbefcc0SGleb Smirnoff goto dropfrag;
3871dbefcc0SGleb Smirnoff
3881dbefcc0SGleb Smirnoff /*
3891dbefcc0SGleb Smirnoff * Find a segment which begins after this one does.
3901dbefcc0SGleb Smirnoff */
3911dbefcc0SGleb Smirnoff for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
3921dbefcc0SGleb Smirnoff if (ntohs(GETIP(q)->ip_off) > ntohs(ip->ip_off))
3931dbefcc0SGleb Smirnoff break;
3941dbefcc0SGleb Smirnoff
3951dbefcc0SGleb Smirnoff /*
3961dbefcc0SGleb Smirnoff * If there is a preceding segment, it may provide some of
3971dbefcc0SGleb Smirnoff * our data already. If so, drop the data from the incoming
3981dbefcc0SGleb Smirnoff * segment. If it provides all of our data, drop us, otherwise
3991dbefcc0SGleb Smirnoff * stick new segment in the proper place.
4001dbefcc0SGleb Smirnoff *
4011dbefcc0SGleb Smirnoff * If some of the data is dropped from the preceding
4021dbefcc0SGleb Smirnoff * segment, then it's checksum is invalidated.
4031dbefcc0SGleb Smirnoff */
4041dbefcc0SGleb Smirnoff if (p) {
4051dbefcc0SGleb Smirnoff i = ntohs(GETIP(p)->ip_off) + ntohs(GETIP(p)->ip_len) -
4061dbefcc0SGleb Smirnoff ntohs(ip->ip_off);
4071dbefcc0SGleb Smirnoff if (i > 0) {
4081dbefcc0SGleb Smirnoff if (i >= ntohs(ip->ip_len))
4091dbefcc0SGleb Smirnoff goto dropfrag;
4101dbefcc0SGleb Smirnoff m_adj(m, i);
4111dbefcc0SGleb Smirnoff m->m_pkthdr.csum_flags = 0;
4121dbefcc0SGleb Smirnoff ip->ip_off = htons(ntohs(ip->ip_off) + i);
4131dbefcc0SGleb Smirnoff ip->ip_len = htons(ntohs(ip->ip_len) - i);
4141dbefcc0SGleb Smirnoff }
4151dbefcc0SGleb Smirnoff m->m_nextpkt = p->m_nextpkt;
4161dbefcc0SGleb Smirnoff p->m_nextpkt = m;
4171dbefcc0SGleb Smirnoff } else {
4181dbefcc0SGleb Smirnoff m->m_nextpkt = fp->ipq_frags;
4191dbefcc0SGleb Smirnoff fp->ipq_frags = m;
4201dbefcc0SGleb Smirnoff }
4211dbefcc0SGleb Smirnoff
4221dbefcc0SGleb Smirnoff /*
4231dbefcc0SGleb Smirnoff * While we overlap succeeding segments trim them or,
4241dbefcc0SGleb Smirnoff * if they are completely covered, dequeue them.
4251dbefcc0SGleb Smirnoff */
4261dbefcc0SGleb Smirnoff for (; q != NULL && ntohs(ip->ip_off) + ntohs(ip->ip_len) >
4271dbefcc0SGleb Smirnoff ntohs(GETIP(q)->ip_off); q = nq) {
4281dbefcc0SGleb Smirnoff i = (ntohs(ip->ip_off) + ntohs(ip->ip_len)) -
4291dbefcc0SGleb Smirnoff ntohs(GETIP(q)->ip_off);
4301dbefcc0SGleb Smirnoff if (i < ntohs(GETIP(q)->ip_len)) {
4311dbefcc0SGleb Smirnoff GETIP(q)->ip_len = htons(ntohs(GETIP(q)->ip_len) - i);
4321dbefcc0SGleb Smirnoff GETIP(q)->ip_off = htons(ntohs(GETIP(q)->ip_off) + i);
4331dbefcc0SGleb Smirnoff m_adj(q, i);
4341dbefcc0SGleb Smirnoff q->m_pkthdr.csum_flags = 0;
4351dbefcc0SGleb Smirnoff break;
4361dbefcc0SGleb Smirnoff }
4371dbefcc0SGleb Smirnoff nq = q->m_nextpkt;
4381dbefcc0SGleb Smirnoff m->m_nextpkt = nq;
4391dbefcc0SGleb Smirnoff IPSTAT_INC(ips_fragdropped);
4401dbefcc0SGleb Smirnoff fp->ipq_nfrags--;
4417b9c5eb0SJonathan T. Looney atomic_subtract_int(&nfrags, 1);
4421dbefcc0SGleb Smirnoff m_freem(q);
4431dbefcc0SGleb Smirnoff }
4441dbefcc0SGleb Smirnoff
4451dbefcc0SGleb Smirnoff /*
4461dbefcc0SGleb Smirnoff * Check for complete reassembly and perform frag per packet
4471dbefcc0SGleb Smirnoff * limiting.
4481dbefcc0SGleb Smirnoff *
4491dbefcc0SGleb Smirnoff * Frag limiting is performed here so that the nth frag has
4501dbefcc0SGleb Smirnoff * a chance to complete the packet before we drop the packet.
4511dbefcc0SGleb Smirnoff * As a result, n+1 frags are actually allowed per packet, but
4521dbefcc0SGleb Smirnoff * only n will ever be stored. (n = maxfragsperpacket.)
4531dbefcc0SGleb Smirnoff *
4541dbefcc0SGleb Smirnoff */
4551dbefcc0SGleb Smirnoff next = 0;
4561dbefcc0SGleb Smirnoff for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
4571dbefcc0SGleb Smirnoff if (ntohs(GETIP(q)->ip_off) != next) {
4581dbefcc0SGleb Smirnoff if (fp->ipq_nfrags > V_maxfragsperpacket)
459ff790bbaSJonathan T. Looney ipq_drop(&V_ipq[hash], fp);
4601dbefcc0SGleb Smirnoff goto done;
4611dbefcc0SGleb Smirnoff }
4621dbefcc0SGleb Smirnoff next += ntohs(GETIP(q)->ip_len);
4631dbefcc0SGleb Smirnoff }
4641dbefcc0SGleb Smirnoff /* Make sure the last packet didn't have the IP_MF flag */
4651dbefcc0SGleb Smirnoff if (p->m_flags & M_IP_FRAG) {
4661dbefcc0SGleb Smirnoff if (fp->ipq_nfrags > V_maxfragsperpacket)
467ff790bbaSJonathan T. Looney ipq_drop(&V_ipq[hash], fp);
4681dbefcc0SGleb Smirnoff goto done;
4691dbefcc0SGleb Smirnoff }
4701dbefcc0SGleb Smirnoff
4711dbefcc0SGleb Smirnoff /*
4721dbefcc0SGleb Smirnoff * Reassembly is complete. Make sure the packet is a sane size.
4731dbefcc0SGleb Smirnoff */
4741dbefcc0SGleb Smirnoff q = fp->ipq_frags;
4751dbefcc0SGleb Smirnoff ip = GETIP(q);
4761dbefcc0SGleb Smirnoff if (next + (ip->ip_hl << 2) > IP_MAXPACKET) {
4771dbefcc0SGleb Smirnoff IPSTAT_INC(ips_toolong);
478ff790bbaSJonathan T. Looney ipq_drop(&V_ipq[hash], fp);
4791dbefcc0SGleb Smirnoff goto done;
4801dbefcc0SGleb Smirnoff }
4811dbefcc0SGleb Smirnoff
4821dbefcc0SGleb Smirnoff /*
4831dbefcc0SGleb Smirnoff * Concatenate fragments.
4841dbefcc0SGleb Smirnoff */
4851dbefcc0SGleb Smirnoff m = q;
4861dbefcc0SGleb Smirnoff t = m->m_next;
4871dbefcc0SGleb Smirnoff m->m_next = NULL;
4881dbefcc0SGleb Smirnoff m_cat(m, t);
4891dbefcc0SGleb Smirnoff nq = q->m_nextpkt;
4901dbefcc0SGleb Smirnoff q->m_nextpkt = NULL;
4911dbefcc0SGleb Smirnoff for (q = nq; q != NULL; q = nq) {
4921dbefcc0SGleb Smirnoff nq = q->m_nextpkt;
4931dbefcc0SGleb Smirnoff q->m_nextpkt = NULL;
4941dbefcc0SGleb Smirnoff m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;
4951dbefcc0SGleb Smirnoff m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;
49609b0b8c0SNavdeep Parhar m_demote_pkthdr(q);
4971dbefcc0SGleb Smirnoff m_cat(m, q);
4981dbefcc0SGleb Smirnoff }
4991dbefcc0SGleb Smirnoff /*
5001dbefcc0SGleb Smirnoff * In order to do checksumming faster we do 'end-around carry' here
5011dbefcc0SGleb Smirnoff * (and not in for{} loop), though it implies we are not going to
5021dbefcc0SGleb Smirnoff * reassemble more than 64k fragments.
5031dbefcc0SGleb Smirnoff */
5041dbefcc0SGleb Smirnoff while (m->m_pkthdr.csum_data & 0xffff0000)
5051dbefcc0SGleb Smirnoff m->m_pkthdr.csum_data = (m->m_pkthdr.csum_data & 0xffff) +
5061dbefcc0SGleb Smirnoff (m->m_pkthdr.csum_data >> 16);
5077b9c5eb0SJonathan T. Looney atomic_subtract_int(&nfrags, fp->ipq_nfrags);
5081dbefcc0SGleb Smirnoff #ifdef MAC
5091dbefcc0SGleb Smirnoff mac_ipq_reassemble(fp, m);
5101dbefcc0SGleb Smirnoff mac_ipq_destroy(fp);
5111dbefcc0SGleb Smirnoff #endif
5121dbefcc0SGleb Smirnoff
5131dbefcc0SGleb Smirnoff /*
5141dbefcc0SGleb Smirnoff * Create header for new ip packet by modifying header of first
5151dbefcc0SGleb Smirnoff * packet; dequeue and discard fragment reassembly header.
5161dbefcc0SGleb Smirnoff * Make header visible.
5171dbefcc0SGleb Smirnoff */
5181dbefcc0SGleb Smirnoff ip->ip_len = htons((ip->ip_hl << 2) + next);
5191dbefcc0SGleb Smirnoff ip->ip_src = fp->ipq_src;
5201dbefcc0SGleb Smirnoff ip->ip_dst = fp->ipq_dst;
5211dbefcc0SGleb Smirnoff TAILQ_REMOVE(head, fp, ipq_list);
522ff790bbaSJonathan T. Looney V_ipq[hash].count--;
5231dbefcc0SGleb Smirnoff uma_zfree(V_ipq_zone, fp);
5241dbefcc0SGleb Smirnoff m->m_len += (ip->ip_hl << 2);
5251dbefcc0SGleb Smirnoff m->m_data -= (ip->ip_hl << 2);
5261dbefcc0SGleb Smirnoff /* some debugging cruft by sklower, below, will go away soon */
527a55383e7SHans Petter Selasky if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
5281dbefcc0SGleb Smirnoff m_fixhdr(m);
529a55383e7SHans Petter Selasky /* set valid receive interface pointer */
530a55383e7SHans Petter Selasky m->m_pkthdr.rcvif = srcifp;
531a55383e7SHans Petter Selasky }
5321dbefcc0SGleb Smirnoff IPSTAT_INC(ips_reassembled);
533a30cb315SGleb Smirnoff ipreass_reschedule(&V_ipq[hash]);
5341dbefcc0SGleb Smirnoff IPQ_UNLOCK(hash);
5351dbefcc0SGleb Smirnoff
5361dbefcc0SGleb Smirnoff #ifdef RSS
5371dbefcc0SGleb Smirnoff /*
5381dbefcc0SGleb Smirnoff * Query the RSS layer for the flowid / flowtype for the
5391dbefcc0SGleb Smirnoff * mbuf payload.
5401dbefcc0SGleb Smirnoff *
5411dbefcc0SGleb Smirnoff * For now, just assume we have to calculate a new one.
5421dbefcc0SGleb Smirnoff * Later on we should check to see if the assigned flowid matches
5431dbefcc0SGleb Smirnoff * what RSS wants for the given IP protocol and if so, just keep it.
5441dbefcc0SGleb Smirnoff *
5451dbefcc0SGleb Smirnoff * We then queue into the relevant netisr so it can be dispatched
5461dbefcc0SGleb Smirnoff * to the correct CPU.
5471dbefcc0SGleb Smirnoff *
5481dbefcc0SGleb Smirnoff * Note - this may return 1, which means the flowid in the mbuf
5491dbefcc0SGleb Smirnoff * is correct for the configured RSS hash types and can be used.
5501dbefcc0SGleb Smirnoff */
5511dbefcc0SGleb Smirnoff if (rss_mbuf_software_hash_v4(m, 0, &rss_hash, &rss_type) == 0) {
5521dbefcc0SGleb Smirnoff m->m_pkthdr.flowid = rss_hash;
5531dbefcc0SGleb Smirnoff M_HASHTYPE_SET(m, rss_type);
5541dbefcc0SGleb Smirnoff }
5551dbefcc0SGleb Smirnoff
5561dbefcc0SGleb Smirnoff /*
5571dbefcc0SGleb Smirnoff * Queue/dispatch for reprocessing.
5581dbefcc0SGleb Smirnoff *
5591dbefcc0SGleb Smirnoff * Note: this is much slower than just handling the frame in the
5601dbefcc0SGleb Smirnoff * current receive context. It's likely worth investigating
5611dbefcc0SGleb Smirnoff * why this is.
5621dbefcc0SGleb Smirnoff */
5631dbefcc0SGleb Smirnoff netisr_dispatch(NETISR_IP_DIRECT, m);
5641dbefcc0SGleb Smirnoff return (NULL);
5651dbefcc0SGleb Smirnoff #endif
5661dbefcc0SGleb Smirnoff
5671dbefcc0SGleb Smirnoff /* Handle in-line */
5681dbefcc0SGleb Smirnoff return (m);
5691dbefcc0SGleb Smirnoff
5701dbefcc0SGleb Smirnoff dropfrag:
5711dbefcc0SGleb Smirnoff IPSTAT_INC(ips_fragdropped);
5727b9c5eb0SJonathan T. Looney if (fp != NULL) {
5731dbefcc0SGleb Smirnoff fp->ipq_nfrags--;
5747b9c5eb0SJonathan T. Looney atomic_subtract_int(&nfrags, 1);
5757b9c5eb0SJonathan T. Looney }
5761dbefcc0SGleb Smirnoff m_freem(m);
5771dbefcc0SGleb Smirnoff done:
5781dbefcc0SGleb Smirnoff IPQ_UNLOCK(hash);
5791dbefcc0SGleb Smirnoff return (NULL);
5801dbefcc0SGleb Smirnoff
5811dbefcc0SGleb Smirnoff #undef GETIP
5821dbefcc0SGleb Smirnoff }
5831dbefcc0SGleb Smirnoff
5841dbefcc0SGleb Smirnoff /*
585a30cb315SGleb Smirnoff * Timer expired on a bucket.
586a30cb315SGleb Smirnoff * There should be at least one ipq to be timed out.
587160f01f0SGleb Smirnoff */
588160f01f0SGleb Smirnoff static void
ipreass_callout(void * arg)589a30cb315SGleb Smirnoff ipreass_callout(void *arg)
590160f01f0SGleb Smirnoff {
591a30cb315SGleb Smirnoff struct ipqbucket *bucket = arg;
592a30cb315SGleb Smirnoff struct ipq *fp;
593160f01f0SGleb Smirnoff
594a30cb315SGleb Smirnoff IPQ_BUCKET_LOCK_ASSERT(bucket);
595a30cb315SGleb Smirnoff MPASS(atomic_load_int(&nfrags) > 0);
596160f01f0SGleb Smirnoff
597a30cb315SGleb Smirnoff CURVNET_SET(bucket->vnet);
598a30cb315SGleb Smirnoff fp = TAILQ_LAST(&bucket->head, ipqhead);
59915b73a2aSGleb Smirnoff KASSERT(fp != NULL && fp->ipq_expire <= time_uptime,
60013018bfaSGleb Smirnoff ("%s: stray callout on bucket %p, %ju < %ju", __func__, bucket,
60113018bfaSGleb Smirnoff fp ? (uintmax_t)fp->ipq_expire : 0, (uintmax_t)time_uptime));
602a30cb315SGleb Smirnoff
60315b73a2aSGleb Smirnoff while (fp != NULL && fp->ipq_expire <= time_uptime) {
604a30cb315SGleb Smirnoff ipq_timeout(bucket, fp);
605a30cb315SGleb Smirnoff fp = TAILQ_LAST(&bucket->head, ipqhead);
606160f01f0SGleb Smirnoff }
607a30cb315SGleb Smirnoff ipreass_reschedule(bucket);
608160f01f0SGleb Smirnoff CURVNET_RESTORE();
609160f01f0SGleb Smirnoff }
610160f01f0SGleb Smirnoff
611160f01f0SGleb Smirnoff static void
ipreass_reschedule(struct ipqbucket * bucket)612a30cb315SGleb Smirnoff ipreass_reschedule(struct ipqbucket *bucket)
613160f01f0SGleb Smirnoff {
614a30cb315SGleb Smirnoff struct ipq *fp;
615160f01f0SGleb Smirnoff
616a30cb315SGleb Smirnoff IPQ_BUCKET_LOCK_ASSERT(bucket);
617a30cb315SGleb Smirnoff
618a30cb315SGleb Smirnoff if ((fp = TAILQ_LAST(&bucket->head, ipqhead)) != NULL) {
619a30cb315SGleb Smirnoff time_t t;
620a30cb315SGleb Smirnoff
621a30cb315SGleb Smirnoff /* Protect against time_uptime tick. */
622a30cb315SGleb Smirnoff t = fp->ipq_expire - time_uptime;
623a30cb315SGleb Smirnoff t = (t > 0) ? t : 1;
624a30cb315SGleb Smirnoff callout_reset_sbt(&bucket->timer, SBT_1S * t, SBT_1S,
625a30cb315SGleb Smirnoff ipreass_callout, bucket, 0);
626a30cb315SGleb Smirnoff } else
627a30cb315SGleb Smirnoff callout_stop(&bucket->timer);
628160f01f0SGleb Smirnoff }
62964981536SGleb Smirnoff
63064981536SGleb Smirnoff static void
ipreass_drain_vnet(void)63164981536SGleb Smirnoff ipreass_drain_vnet(void)
63264981536SGleb Smirnoff {
63329b4b63cSGleb Smirnoff u_int dropped = 0;
63464981536SGleb Smirnoff
6351494f477SGleb Smirnoff for (int i = 0; i < V_ipq_hashsize; i++) {
63629b4b63cSGleb Smirnoff bool resched;
63729b4b63cSGleb Smirnoff
63864981536SGleb Smirnoff IPQ_LOCK(i);
63929b4b63cSGleb Smirnoff resched = !TAILQ_EMPTY(&V_ipq[i].head);
64029b4b63cSGleb Smirnoff while(!TAILQ_EMPTY(&V_ipq[i].head)) {
64129b4b63cSGleb Smirnoff struct ipq *fp = TAILQ_FIRST(&V_ipq[i].head);
64229b4b63cSGleb Smirnoff
64329b4b63cSGleb Smirnoff dropped += fp->ipq_nfrags;
64429b4b63cSGleb Smirnoff ipq_free(&V_ipq[i], fp);
64529b4b63cSGleb Smirnoff }
64629b4b63cSGleb Smirnoff if (resched)
64729b4b63cSGleb Smirnoff ipreass_reschedule(&V_ipq[i]);
64864981536SGleb Smirnoff KASSERT(V_ipq[i].count == 0,
64964981536SGleb Smirnoff ("%s: V_ipq[%d] count %d (V_ipq=%p)", __func__, i,
65064981536SGleb Smirnoff V_ipq[i].count, V_ipq));
65164981536SGleb Smirnoff IPQ_UNLOCK(i);
65264981536SGleb Smirnoff }
65329b4b63cSGleb Smirnoff IPSTAT_ADD(ips_fragdropped, dropped);
65464981536SGleb Smirnoff }
655160f01f0SGleb Smirnoff
656160f01f0SGleb Smirnoff /*
65781a34d37SGleb Smirnoff * Drain off all datagram fragments.
65881a34d37SGleb Smirnoff */
65981a34d37SGleb Smirnoff static void
ipreass_drain(void)66081a34d37SGleb Smirnoff ipreass_drain(void)
66181a34d37SGleb Smirnoff {
66281a34d37SGleb Smirnoff VNET_ITERATOR_DECL(vnet_iter);
66381a34d37SGleb Smirnoff
664bd7b2f95SKristof Provost VNET_LIST_RLOCK();
66581a34d37SGleb Smirnoff VNET_FOREACH(vnet_iter) {
66681a34d37SGleb Smirnoff CURVNET_SET(vnet_iter);
66764981536SGleb Smirnoff ipreass_drain_vnet();
66881a34d37SGleb Smirnoff CURVNET_RESTORE();
66981a34d37SGleb Smirnoff }
670bd7b2f95SKristof Provost VNET_LIST_RUNLOCK();
67181a34d37SGleb Smirnoff }
67281a34d37SGleb Smirnoff
673*8ee127efSSHENGYI HONG static void
ipreass_drain_lowmem(void * arg __unused,int flags __unused)674*8ee127efSSHENGYI HONG ipreass_drain_lowmem(void *arg __unused, int flags __unused)
675*8ee127efSSHENGYI HONG {
676*8ee127efSSHENGYI HONG ipreass_drain();
677*8ee127efSSHENGYI HONG }
67881a34d37SGleb Smirnoff
67981a34d37SGleb Smirnoff /*
6801dbefcc0SGleb Smirnoff * Initialize IP reassembly structures.
6811dbefcc0SGleb Smirnoff */
6821494f477SGleb Smirnoff MALLOC_DEFINE(M_IPREASS_HASH, "IP reass", "IP packet reassembly hash headers");
6831dbefcc0SGleb Smirnoff void
ipreass_vnet_init(void)684aea0cd04SGleb Smirnoff ipreass_vnet_init(void)
6851dbefcc0SGleb Smirnoff {
686ff790bbaSJonathan T. Looney int max;
6871dbefcc0SGleb Smirnoff
6881494f477SGleb Smirnoff V_ipq_hashsize = IPREASS_NHASH;
6891494f477SGleb Smirnoff TUNABLE_INT_FETCH("net.inet.ip.reass_hashsize", &V_ipq_hashsize);
6901494f477SGleb Smirnoff V_ipq = malloc(sizeof(struct ipqbucket) * V_ipq_hashsize,
6911494f477SGleb Smirnoff M_IPREASS_HASH, M_WAITOK);
6921494f477SGleb Smirnoff
6931494f477SGleb Smirnoff for (int i = 0; i < V_ipq_hashsize; i++) {
6941dbefcc0SGleb Smirnoff TAILQ_INIT(&V_ipq[i].head);
6951dbefcc0SGleb Smirnoff mtx_init(&V_ipq[i].lock, "IP reassembly", NULL,
6961494f477SGleb Smirnoff MTX_DEF | MTX_DUPOK | MTX_NEW);
697a30cb315SGleb Smirnoff callout_init_mtx(&V_ipq[i].timer, &V_ipq[i].lock, 0);
698ff790bbaSJonathan T. Looney V_ipq[i].count = 0;
699a30cb315SGleb Smirnoff #ifdef VIMAGE
700a30cb315SGleb Smirnoff V_ipq[i].vnet = curvnet;
701a30cb315SGleb Smirnoff #endif
7021dbefcc0SGleb Smirnoff }
703c047fd1bSGleb Smirnoff V_ipq_hashseed = arc4random();
7041dbefcc0SGleb Smirnoff V_maxfragsperpacket = 16;
7051dbefcc0SGleb Smirnoff V_ipq_zone = uma_zcreate("ipq", sizeof(struct ipq), NULL, NULL, NULL,
7061dbefcc0SGleb Smirnoff NULL, UMA_ALIGN_PTR, 0);
707a967df1cSJonathan T. Looney max = IP_MAXFRAGPACKETS;
708ff790bbaSJonathan T. Looney max = uma_zone_set_max(V_ipq_zone, max);
7091494f477SGleb Smirnoff V_ipreass_maxbucketsize = imax(max / (V_ipq_hashsize / 2), 1);
710aea0cd04SGleb Smirnoff }
7111dbefcc0SGleb Smirnoff
712aea0cd04SGleb Smirnoff void
ipreass_init(void)713aea0cd04SGleb Smirnoff ipreass_init(void)
714aea0cd04SGleb Smirnoff {
715aea0cd04SGleb Smirnoff
716a967df1cSJonathan T. Looney maxfrags = IP_MAXFRAGS;
7171dbefcc0SGleb Smirnoff EVENTHANDLER_REGISTER(nmbclusters_change, ipreass_zone_change,
7181dbefcc0SGleb Smirnoff NULL, EVENTHANDLER_PRI_ANY);
719*8ee127efSSHENGYI HONG EVENTHANDLER_REGISTER(vm_lowmem, ipreass_drain_lowmem, NULL,
72081a34d37SGleb Smirnoff LOWMEM_PRI_DEFAULT);
721*8ee127efSSHENGYI HONG EVENTHANDLER_REGISTER(mbuf_lowmem, ipreass_drain_lowmem, NULL,
72281a34d37SGleb Smirnoff LOWMEM_PRI_DEFAULT);
7231dbefcc0SGleb Smirnoff }
7241dbefcc0SGleb Smirnoff
725a55383e7SHans Petter Selasky /*
726a55383e7SHans Petter Selasky * Drain off all datagram fragments belonging to
727a55383e7SHans Petter Selasky * the given network interface.
728a55383e7SHans Petter Selasky */
729a55383e7SHans Petter Selasky static void
ipreass_cleanup(void * arg __unused,struct ifnet * ifp)730a55383e7SHans Petter Selasky ipreass_cleanup(void *arg __unused, struct ifnet *ifp)
731a55383e7SHans Petter Selasky {
732a55383e7SHans Petter Selasky struct ipq *fp, *temp;
733a55383e7SHans Petter Selasky struct mbuf *m;
734a55383e7SHans Petter Selasky int i;
735a55383e7SHans Petter Selasky
736a55383e7SHans Petter Selasky KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
737a55383e7SHans Petter Selasky
7386e6b5143SBjoern A. Zeeb CURVNET_SET_QUIET(ifp->if_vnet);
7396e6b5143SBjoern A. Zeeb
740a55383e7SHans Petter Selasky /*
741a55383e7SHans Petter Selasky * Skip processing if IPv4 reassembly is not initialised or
742a55383e7SHans Petter Selasky * torn down by ipreass_destroy().
743a55383e7SHans Petter Selasky */
7446e6b5143SBjoern A. Zeeb if (V_ipq_zone == NULL) {
7456e6b5143SBjoern A. Zeeb CURVNET_RESTORE();
746a55383e7SHans Petter Selasky return;
7476e6b5143SBjoern A. Zeeb }
748a55383e7SHans Petter Selasky
7491494f477SGleb Smirnoff for (i = 0; i < V_ipq_hashsize; i++) {
750a55383e7SHans Petter Selasky IPQ_LOCK(i);
751a55383e7SHans Petter Selasky /* Scan fragment list. */
752a55383e7SHans Petter Selasky TAILQ_FOREACH_SAFE(fp, &V_ipq[i].head, ipq_list, temp) {
753a55383e7SHans Petter Selasky for (m = fp->ipq_frags; m != NULL; m = m->m_nextpkt) {
754a55383e7SHans Petter Selasky /* clear no longer valid rcvif pointer */
755a55383e7SHans Petter Selasky if (m->m_pkthdr.rcvif == ifp)
756a55383e7SHans Petter Selasky m->m_pkthdr.rcvif = NULL;
757a55383e7SHans Petter Selasky }
758a55383e7SHans Petter Selasky }
759a55383e7SHans Petter Selasky IPQ_UNLOCK(i);
760a55383e7SHans Petter Selasky }
761a55383e7SHans Petter Selasky CURVNET_RESTORE();
762a55383e7SHans Petter Selasky }
763a55383e7SHans Petter Selasky EVENTHANDLER_DEFINE(ifnet_departure_event, ipreass_cleanup, NULL, 0);
764a55383e7SHans Petter Selasky
7651dbefcc0SGleb Smirnoff #ifdef VIMAGE
7661dbefcc0SGleb Smirnoff /*
7671dbefcc0SGleb Smirnoff * Destroy IP reassembly structures.
7681dbefcc0SGleb Smirnoff */
7691dbefcc0SGleb Smirnoff void
ipreass_destroy(void)7701dbefcc0SGleb Smirnoff ipreass_destroy(void)
7711dbefcc0SGleb Smirnoff {
7721dbefcc0SGleb Smirnoff
77364981536SGleb Smirnoff ipreass_drain_vnet();
7741dbefcc0SGleb Smirnoff uma_zdestroy(V_ipq_zone);
775a55383e7SHans Petter Selasky V_ipq_zone = NULL;
7761494f477SGleb Smirnoff for (int i = 0; i < V_ipq_hashsize; i++)
7771dbefcc0SGleb Smirnoff mtx_destroy(&V_ipq[i].lock);
778c8bc8741SGleb Smirnoff free(V_ipq, M_IPREASS_HASH);
7791dbefcc0SGleb Smirnoff }
7801dbefcc0SGleb Smirnoff #endif
7811dbefcc0SGleb Smirnoff
7821dbefcc0SGleb Smirnoff /*
7831dbefcc0SGleb Smirnoff * After maxnipq has been updated, propagate the change to UMA. The UMA zone
7841dbefcc0SGleb Smirnoff * max has slightly different semantics than the sysctl, for historical
7851dbefcc0SGleb Smirnoff * reasons.
7861dbefcc0SGleb Smirnoff */
7871dbefcc0SGleb Smirnoff static void
ipreass_drain_tomax(void)7881dbefcc0SGleb Smirnoff ipreass_drain_tomax(void)
7891dbefcc0SGleb Smirnoff {
790ff790bbaSJonathan T. Looney struct ipq *fp;
7911dbefcc0SGleb Smirnoff int target;
7921dbefcc0SGleb Smirnoff
7931dbefcc0SGleb Smirnoff /*
794ff790bbaSJonathan T. Looney * Make sure each bucket is under the new limit. If
795ff790bbaSJonathan T. Looney * necessary, drop enough of the oldest elements from
796ff790bbaSJonathan T. Looney * each bucket to get under the new limit.
797ff790bbaSJonathan T. Looney */
7981494f477SGleb Smirnoff for (int i = 0; i < V_ipq_hashsize; i++) {
799ff790bbaSJonathan T. Looney IPQ_LOCK(i);
800ff790bbaSJonathan T. Looney while (V_ipq[i].count > V_ipreass_maxbucketsize &&
801ff790bbaSJonathan T. Looney (fp = TAILQ_LAST(&V_ipq[i].head, ipqhead)) != NULL)
802ff790bbaSJonathan T. Looney ipq_timeout(&V_ipq[i], fp);
803a30cb315SGleb Smirnoff ipreass_reschedule(&V_ipq[i]);
804ff790bbaSJonathan T. Looney IPQ_UNLOCK(i);
805ff790bbaSJonathan T. Looney }
806ff790bbaSJonathan T. Looney
807ff790bbaSJonathan T. Looney /*
8081dbefcc0SGleb Smirnoff * If we are over the maximum number of fragments,
8091dbefcc0SGleb Smirnoff * drain off enough to get down to the new limit,
8101dbefcc0SGleb Smirnoff * stripping off last elements on queues. Every
8111dbefcc0SGleb Smirnoff * run we strip the oldest element from each bucket.
8121dbefcc0SGleb Smirnoff */
8131dbefcc0SGleb Smirnoff target = uma_zone_get_max(V_ipq_zone);
8141dbefcc0SGleb Smirnoff while (uma_zone_get_cur(V_ipq_zone) > target) {
8151494f477SGleb Smirnoff for (int i = 0; i < V_ipq_hashsize; i++) {
8161dbefcc0SGleb Smirnoff IPQ_LOCK(i);
8171dbefcc0SGleb Smirnoff fp = TAILQ_LAST(&V_ipq[i].head, ipqhead);
818a30cb315SGleb Smirnoff if (fp != NULL) {
819ff790bbaSJonathan T. Looney ipq_timeout(&V_ipq[i], fp);
820a30cb315SGleb Smirnoff ipreass_reschedule(&V_ipq[i]);
821a30cb315SGleb Smirnoff }
8221dbefcc0SGleb Smirnoff IPQ_UNLOCK(i);
8231dbefcc0SGleb Smirnoff }
8241dbefcc0SGleb Smirnoff }
8251dbefcc0SGleb Smirnoff }
8261dbefcc0SGleb Smirnoff
8271dbefcc0SGleb Smirnoff static void
ipreass_zone_change(void * tag)8281dbefcc0SGleb Smirnoff ipreass_zone_change(void *tag)
8291dbefcc0SGleb Smirnoff {
8307b9c5eb0SJonathan T. Looney VNET_ITERATOR_DECL(vnet_iter);
8317b9c5eb0SJonathan T. Looney int max;
8321dbefcc0SGleb Smirnoff
833a967df1cSJonathan T. Looney maxfrags = IP_MAXFRAGS;
834a967df1cSJonathan T. Looney max = IP_MAXFRAGPACKETS;
8357b9c5eb0SJonathan T. Looney VNET_LIST_RLOCK_NOSLEEP();
8367b9c5eb0SJonathan T. Looney VNET_FOREACH(vnet_iter) {
8377b9c5eb0SJonathan T. Looney CURVNET_SET(vnet_iter);
838ff790bbaSJonathan T. Looney max = uma_zone_set_max(V_ipq_zone, max);
8391494f477SGleb Smirnoff V_ipreass_maxbucketsize = imax(max / (V_ipq_hashsize / 2), 1);
8401dbefcc0SGleb Smirnoff ipreass_drain_tomax();
8417b9c5eb0SJonathan T. Looney CURVNET_RESTORE();
8427b9c5eb0SJonathan T. Looney }
8437b9c5eb0SJonathan T. Looney VNET_LIST_RUNLOCK_NOSLEEP();
8441dbefcc0SGleb Smirnoff }
8451dbefcc0SGleb Smirnoff
8461dbefcc0SGleb Smirnoff /*
8471dbefcc0SGleb Smirnoff * Change the limit on the UMA zone, or disable the fragment allocation
8481dbefcc0SGleb Smirnoff * at all. Since 0 and -1 is a special values here, we need our own handler,
8491dbefcc0SGleb Smirnoff * instead of sysctl_handle_uma_zone_max().
8501dbefcc0SGleb Smirnoff */
8511dbefcc0SGleb Smirnoff static int
sysctl_maxfragpackets(SYSCTL_HANDLER_ARGS)8521dbefcc0SGleb Smirnoff sysctl_maxfragpackets(SYSCTL_HANDLER_ARGS)
8531dbefcc0SGleb Smirnoff {
8541dbefcc0SGleb Smirnoff int error, max;
8551dbefcc0SGleb Smirnoff
8561dbefcc0SGleb Smirnoff if (V_noreass == 0) {
8571dbefcc0SGleb Smirnoff max = uma_zone_get_max(V_ipq_zone);
8581dbefcc0SGleb Smirnoff if (max == 0)
8591dbefcc0SGleb Smirnoff max = -1;
8601dbefcc0SGleb Smirnoff } else
8611dbefcc0SGleb Smirnoff max = 0;
8621dbefcc0SGleb Smirnoff error = sysctl_handle_int(oidp, &max, 0, req);
8631dbefcc0SGleb Smirnoff if (error || !req->newptr)
8641dbefcc0SGleb Smirnoff return (error);
8651dbefcc0SGleb Smirnoff if (max > 0) {
8661dbefcc0SGleb Smirnoff /*
8671dbefcc0SGleb Smirnoff * XXXRW: Might be a good idea to sanity check the argument
8681dbefcc0SGleb Smirnoff * and place an extreme upper bound.
8691dbefcc0SGleb Smirnoff */
8701dbefcc0SGleb Smirnoff max = uma_zone_set_max(V_ipq_zone, max);
8711494f477SGleb Smirnoff V_ipreass_maxbucketsize = imax(max / (V_ipq_hashsize / 2), 1);
8721dbefcc0SGleb Smirnoff ipreass_drain_tomax();
8731dbefcc0SGleb Smirnoff V_noreass = 0;
8741dbefcc0SGleb Smirnoff } else if (max == 0) {
8751dbefcc0SGleb Smirnoff V_noreass = 1;
8761dbefcc0SGleb Smirnoff ipreass_drain();
8771dbefcc0SGleb Smirnoff } else if (max == -1) {
8781dbefcc0SGleb Smirnoff V_noreass = 0;
8791dbefcc0SGleb Smirnoff uma_zone_set_max(V_ipq_zone, 0);
880ff790bbaSJonathan T. Looney V_ipreass_maxbucketsize = INT_MAX;
8811dbefcc0SGleb Smirnoff } else
8821dbefcc0SGleb Smirnoff return (EINVAL);
8831dbefcc0SGleb Smirnoff return (0);
8841dbefcc0SGleb Smirnoff }
8851dbefcc0SGleb Smirnoff
8861dbefcc0SGleb Smirnoff /*
8871dbefcc0SGleb Smirnoff * Seek for old fragment queue header that can be reused. Try to
8881dbefcc0SGleb Smirnoff * reuse a header from currently locked hash bucket.
8891dbefcc0SGleb Smirnoff */
8901dbefcc0SGleb Smirnoff static struct ipq *
ipq_reuse(int start)8911dbefcc0SGleb Smirnoff ipq_reuse(int start)
8921dbefcc0SGleb Smirnoff {
8931dbefcc0SGleb Smirnoff struct ipq *fp;
894ff790bbaSJonathan T. Looney int bucket, i;
8951dbefcc0SGleb Smirnoff
8961dbefcc0SGleb Smirnoff IPQ_LOCK_ASSERT(start);
8971dbefcc0SGleb Smirnoff
8981494f477SGleb Smirnoff for (i = 0; i < V_ipq_hashsize; i++) {
8991494f477SGleb Smirnoff bucket = (start + i) % V_ipq_hashsize;
900ff790bbaSJonathan T. Looney if (bucket != start && IPQ_TRYLOCK(bucket) == 0)
9011dbefcc0SGleb Smirnoff continue;
902ff790bbaSJonathan T. Looney fp = TAILQ_LAST(&V_ipq[bucket].head, ipqhead);
9031dbefcc0SGleb Smirnoff if (fp) {
9041dbefcc0SGleb Smirnoff struct mbuf *m;
9051dbefcc0SGleb Smirnoff
9061dbefcc0SGleb Smirnoff IPSTAT_ADD(ips_fragtimeout, fp->ipq_nfrags);
9077b9c5eb0SJonathan T. Looney atomic_subtract_int(&nfrags, fp->ipq_nfrags);
9081dbefcc0SGleb Smirnoff while (fp->ipq_frags) {
9091dbefcc0SGleb Smirnoff m = fp->ipq_frags;
9101dbefcc0SGleb Smirnoff fp->ipq_frags = m->m_nextpkt;
9111dbefcc0SGleb Smirnoff m_freem(m);
9121dbefcc0SGleb Smirnoff }
913ff790bbaSJonathan T. Looney TAILQ_REMOVE(&V_ipq[bucket].head, fp, ipq_list);
914ff790bbaSJonathan T. Looney V_ipq[bucket].count--;
915a30cb315SGleb Smirnoff ipreass_reschedule(&V_ipq[bucket]);
916ff790bbaSJonathan T. Looney if (bucket != start)
917ff790bbaSJonathan T. Looney IPQ_UNLOCK(bucket);
918ff790bbaSJonathan T. Looney break;
919ff790bbaSJonathan T. Looney }
920ff790bbaSJonathan T. Looney if (bucket != start)
921ff790bbaSJonathan T. Looney IPQ_UNLOCK(bucket);
922ff790bbaSJonathan T. Looney }
9231dbefcc0SGleb Smirnoff IPQ_LOCK_ASSERT(start);
9241dbefcc0SGleb Smirnoff return (fp);
9251dbefcc0SGleb Smirnoff }
9261dbefcc0SGleb Smirnoff
9271dbefcc0SGleb Smirnoff /*
9281dbefcc0SGleb Smirnoff * Free a fragment reassembly header and all associated datagrams.
9291dbefcc0SGleb Smirnoff */
9301dbefcc0SGleb Smirnoff static void
ipq_free(struct ipqbucket * bucket,struct ipq * fp)931ff790bbaSJonathan T. Looney ipq_free(struct ipqbucket *bucket, struct ipq *fp)
9321dbefcc0SGleb Smirnoff {
9331dbefcc0SGleb Smirnoff struct mbuf *q;
9341dbefcc0SGleb Smirnoff
9357b9c5eb0SJonathan T. Looney atomic_subtract_int(&nfrags, fp->ipq_nfrags);
9361dbefcc0SGleb Smirnoff while (fp->ipq_frags) {
9371dbefcc0SGleb Smirnoff q = fp->ipq_frags;
9381dbefcc0SGleb Smirnoff fp->ipq_frags = q->m_nextpkt;
9391dbefcc0SGleb Smirnoff m_freem(q);
9401dbefcc0SGleb Smirnoff }
941ff790bbaSJonathan T. Looney TAILQ_REMOVE(&bucket->head, fp, ipq_list);
942ff790bbaSJonathan T. Looney bucket->count--;
9431dbefcc0SGleb Smirnoff uma_zfree(V_ipq_zone, fp);
9441dbefcc0SGleb Smirnoff }
945ff790bbaSJonathan T. Looney
946ff790bbaSJonathan T. Looney /*
947ff790bbaSJonathan T. Looney * Get or set the maximum number of reassembly queues per bucket.
948ff790bbaSJonathan T. Looney */
949ff790bbaSJonathan T. Looney static int
sysctl_maxfragbucketsize(SYSCTL_HANDLER_ARGS)950ff790bbaSJonathan T. Looney sysctl_maxfragbucketsize(SYSCTL_HANDLER_ARGS)
951ff790bbaSJonathan T. Looney {
952ff790bbaSJonathan T. Looney int error, max;
953ff790bbaSJonathan T. Looney
954ff790bbaSJonathan T. Looney max = V_ipreass_maxbucketsize;
955ff790bbaSJonathan T. Looney error = sysctl_handle_int(oidp, &max, 0, req);
956ff790bbaSJonathan T. Looney if (error || !req->newptr)
957ff790bbaSJonathan T. Looney return (error);
958ff790bbaSJonathan T. Looney if (max <= 0)
959ff790bbaSJonathan T. Looney return (EINVAL);
960ff790bbaSJonathan T. Looney V_ipreass_maxbucketsize = max;
961ff790bbaSJonathan T. Looney ipreass_drain_tomax();
962ff790bbaSJonathan T. Looney return (0);
963ff790bbaSJonathan T. Looney }
964a30cb315SGleb Smirnoff
965a30cb315SGleb Smirnoff /*
966a30cb315SGleb Smirnoff * Get or set the IP fragment time to live.
967a30cb315SGleb Smirnoff */
968a30cb315SGleb Smirnoff static int
sysctl_fragttl(SYSCTL_HANDLER_ARGS)969a30cb315SGleb Smirnoff sysctl_fragttl(SYSCTL_HANDLER_ARGS)
970a30cb315SGleb Smirnoff {
971a30cb315SGleb Smirnoff u_int ttl;
972a30cb315SGleb Smirnoff int error;
973a30cb315SGleb Smirnoff
974a30cb315SGleb Smirnoff ttl = V_ipfragttl;
975a30cb315SGleb Smirnoff error = sysctl_handle_int(oidp, &ttl, 0, req);
976a30cb315SGleb Smirnoff if (error || !req->newptr)
977a30cb315SGleb Smirnoff return (error);
978a30cb315SGleb Smirnoff
979a30cb315SGleb Smirnoff if (ttl < 1 || ttl > MAXTTL)
980a30cb315SGleb Smirnoff return (EINVAL);
981a30cb315SGleb Smirnoff
982a30cb315SGleb Smirnoff atomic_store_int(&V_ipfragttl, ttl);
983a30cb315SGleb Smirnoff return (0);
984a30cb315SGleb Smirnoff }
985