Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/netinet/ip_input.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/netinet/ip_input.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.1 retrieving revision 1.355.2.6 diff -u -p -r1.1 -r1.355.2.6 --- src/sys/netinet/ip_input.c 1993/03/21 09:45:37 1.1 +++ src/sys/netinet/ip_input.c 2018/03/18 10:57:01 1.355.2.6 @@ -1,7 +1,42 @@ +/* $NetBSD: ip_input.c,v 1.355.2.6 2018/03/18 10:57:01 martin Exp $ */ + /* - * Copyright (c) 1982, 1986, 1988 Regents of the University of California. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * + * This code is derived from software contributed to The NetBSD Foundation + * by Public Access Networks Corporation ("Panix"). It was developed under + * contract to Panix by Eric Haszlakiewicz and Thor Lancelot Simon. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -10,11 +45,33 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -30,30 +87,69 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ip_input.c 7.19 (Berkeley) 5/25/91 + * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ -#include "param.h" -#include "systm.h" -#include "malloc.h" -#include "mbuf.h" -#include "domain.h" -#include "protosw.h" -#include "socket.h" -#include "errno.h" -#include "time.h" -#include "kernel.h" - -#include "../net/if.h" -#include "../net/route.h" - -#include "in.h" -#include "in_systm.h" -#include "ip.h" -#include "in_pcb.h" -#include "in_var.h" -#include "ip_var.h" -#include "ip_icmp.h" +#include +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.355.2.6 2018/03/18 10:57:01 martin Exp $"); + +#ifdef _KERNEL_OPT +#include "opt_inet.h" +#include "opt_compat_netbsd.h" +#include "opt_gateway.h" +#include "opt_ipsec.h" +#include "opt_mrouting.h" +#include "opt_mbuftrace.h" +#include "opt_inet_csum.h" +#include "opt_net_mpsafe.h" +#endif + +#include "arp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* just for gif_ttl */ +#include +#include "gif.h" +#include +#include "gre.h" + +#ifdef MROUTING +#include +#endif +#include + +#ifdef IPSEC +#include +#endif #ifndef IPFORWARDING #ifdef GATEWAY @@ -65,137 +161,419 @@ #ifndef IPSENDREDIRECTS #define IPSENDREDIRECTS 1 #endif +#ifndef IPFORWSRCRT +#define IPFORWSRCRT 0 /* forward source-routed packets */ +#endif +#ifndef IPALLOWSRCRT +#define IPALLOWSRCRT 0 /* allow source-routed packets */ +#endif +#ifndef IPMTUDISC +#define IPMTUDISC 1 +#endif +#ifndef IPMTUDISCTIMEOUT +#define IPMTUDISCTIMEOUT (10 * 60) /* as per RFC 1191 */ +#endif + +#ifdef COMPAT_50 +#include +#include +#endif + +/* + * Note: DIRECTED_BROADCAST is handled this way so that previous + * configuration using this option will Just Work. + */ +#ifndef IPDIRECTEDBCAST +#ifdef DIRECTED_BROADCAST +#define IPDIRECTEDBCAST 1 +#else +#define IPDIRECTEDBCAST 0 +#endif /* DIRECTED_BROADCAST */ +#endif /* IPDIRECTEDBCAST */ int ipforwarding = IPFORWARDING; int ipsendredirects = IPSENDREDIRECTS; +int ip_defttl = IPDEFTTL; +int ip_forwsrcrt = IPFORWSRCRT; +int ip_directedbcast = IPDIRECTEDBCAST; +int ip_allowsrcrt = IPALLOWSRCRT; +int ip_mtudisc = IPMTUDISC; +int ip_mtudisc_timeout = IPMTUDISCTIMEOUT; #ifdef DIAGNOSTIC int ipprintfs = 0; #endif -extern struct domain inetdomain; -extern struct protosw inetsw[]; -u_char ip_protox[IPPROTO_MAX]; -int ipqmaxlen = IFQ_MAXLEN; -struct in_ifaddr *in_ifaddr; /* first inet address */ +int ip_do_randomid = 0; + +/* + * XXX - Setting ip_checkinterface mostly implements the receive side of + * the Strong ES model described in RFC 1122, but since the routing table + * and transmit implementation do not implement the Strong ES model, + * setting this to 1 results in an odd hybrid. + * + * XXX - ip_checkinterface currently must be disabled if you use ipnat + * to translate the destination address to another local interface. + * + * XXX - ip_checkinterface must be disabled if you add IP aliases + * to the loopback interface instead of the interface where the + * packets for those addresses are received. + */ +static int ip_checkinterface __read_mostly = 0; + +struct rttimer_queue *ip_mtudisc_timeout_q = NULL; + +pktqueue_t * ip_pktq __read_mostly; +pfil_head_t * inet_pfil_hook __read_mostly; +ipid_state_t * ip_ids __read_mostly; +percpu_t * ipstat_percpu __read_mostly; + +static percpu_t *ipforward_rt_percpu __cacheline_aligned; + +uint16_t ip_id; + +#ifdef INET_CSUM_COUNTERS +#include + +struct evcnt ip_hwcsum_bad = EVCNT_INITIALIZER(EVCNT_TYPE_MISC, + NULL, "inet", "hwcsum bad"); +struct evcnt ip_hwcsum_ok = EVCNT_INITIALIZER(EVCNT_TYPE_MISC, + NULL, "inet", "hwcsum ok"); +struct evcnt ip_swcsum = EVCNT_INITIALIZER(EVCNT_TYPE_MISC, + NULL, "inet", "swcsum"); + +#define INET_CSUM_COUNTER_INCR(ev) (ev)->ev_count++ + +EVCNT_ATTACH_STATIC(ip_hwcsum_bad); +EVCNT_ATTACH_STATIC(ip_hwcsum_ok); +EVCNT_ATTACH_STATIC(ip_swcsum); + +#else + +#define INET_CSUM_COUNTER_INCR(ev) /* nothing */ + +#endif /* INET_CSUM_COUNTERS */ /* - * We need to save the IP options in case a protocol wants to respond + * Used to save the IP options in case a protocol wants to respond * to an incoming packet over the same route if the packet got here * using IP source routing. This allows connection establishment and * maintenance when the remote end is on a network that is not known * to us. */ -int ip_nhops = 0; -static struct ip_srcrt { - struct in_addr dst; /* final destination */ - char nop; /* one NOP to align */ - char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */ - struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)]; -} ip_srcrt; +struct ip_srcrt { + int isr_nhops; /* number of hops */ + struct in_addr isr_dst; /* final destination */ + char isr_nop; /* one NOP to align */ + char isr_hdr[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN & OFFSET */ + struct in_addr isr_routes[MAX_IPOPTLEN/sizeof(struct in_addr)]; +}; -#ifdef GATEWAY -extern int if_index; -u_long *ip_ifmatrix; +static int ip_drainwanted; + +static void save_rte(struct mbuf *, u_char *, struct in_addr); + +#ifdef MBUFTRACE +struct mowner ip_rx_mowner = MOWNER_INIT("internet", "rx"); +struct mowner ip_tx_mowner = MOWNER_INIT("internet", "tx"); +#endif + +static void ipintr(void *); +static void ip_input(struct mbuf *); +static void ip_forward(struct mbuf *, int, struct ifnet *); +static bool ip_dooptions(struct mbuf *); +static struct in_ifaddr *ip_rtaddr(struct in_addr, struct psref *); +static void sysctl_net_inet_ip_setup(struct sysctllog **); + +static struct in_ifaddr *ip_match_our_address(struct ifnet *, struct ip *, + int *); +static struct in_ifaddr *ip_match_our_address_broadcast(struct ifnet *, + struct ip *); + +#ifdef NET_MPSAFE +#define SOFTNET_LOCK() mutex_enter(softnet_lock) +#define SOFTNET_UNLOCK() mutex_exit(softnet_lock) +#else +#define SOFTNET_LOCK() KASSERT(mutex_owned(softnet_lock)) +#define SOFTNET_UNLOCK() KASSERT(mutex_owned(softnet_lock)) #endif /* * IP initialization: fill in IP protocol switch table. * All protocols not implemented in kernel go to raw IP protocol handler. */ -ip_init() +void +ip_init(void) { - register struct protosw *pr; - register int i; + const struct protosw *pr; + + in_init(); + sysctl_net_inet_ip_setup(NULL); pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); - if (pr == 0) - panic("ip_init"); - for (i = 0; i < IPPROTO_MAX; i++) + KASSERT(pr != NULL); + + ip_pktq = pktq_create(IFQ_MAXLEN, ipintr, NULL); + KASSERT(ip_pktq != NULL); + + for (u_int i = 0; i < IPPROTO_MAX; i++) { ip_protox[i] = pr - inetsw; + } for (pr = inetdomain.dom_protosw; pr < inetdomain.dom_protoswNPROTOSW; pr++) if (pr->pr_domain->dom_family == PF_INET && pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) ip_protox[pr->pr_protocol] = pr - inetsw; - ipq.next = ipq.prev = &ipq; - ip_id = time.tv_sec & 0xffff; - ipintrq.ifq_maxlen = ipqmaxlen; + + ip_reass_init(); + + ip_ids = ip_id_init(); + ip_id = time_uptime & 0xfffff; + #ifdef GATEWAY - i = (if_index + 1) * (if_index + 1) * sizeof (u_long); - if ((ip_ifmatrix = (u_long *) malloc(i, M_RTABLE, M_WAITOK)) == 0) - panic("no memory for ip_ifmatrix"); + ipflow_init(); #endif + + /* Register our Packet Filter hook. */ + inet_pfil_hook = pfil_head_create(PFIL_TYPE_AF, (void *)AF_INET); + KASSERT(inet_pfil_hook != NULL); + +#ifdef MBUFTRACE + MOWNER_ATTACH(&ip_tx_mowner); + MOWNER_ATTACH(&ip_rx_mowner); +#endif /* MBUFTRACE */ + + ipstat_percpu = percpu_alloc(sizeof(uint64_t) * IP_NSTATS); + ipforward_rt_percpu = percpu_alloc(sizeof(struct route)); + ip_mtudisc_timeout_q = rt_timer_queue_create(ip_mtudisc_timeout); +} + +static struct in_ifaddr * +ip_match_our_address(struct ifnet *ifp, struct ip *ip, int *downmatch) +{ + struct in_ifaddr *ia = NULL; + int checkif; + + /* + * Enable a consistency check between the destination address + * and the arrival interface for a unicast packet (the RFC 1122 + * strong ES model) if IP forwarding is disabled and the packet + * is not locally generated. + * + * XXX - Checking also should be disabled if the destination + * address is ipnat'ed to a different interface. + * + * XXX - Checking is incompatible with IP aliases added + * to the loopback interface instead of the interface where + * the packets are received. + * + * XXX - We need to add a per ifaddr flag for this so that + * we get finer grain control. + */ + checkif = ip_checkinterface && (ipforwarding == 0) && + (ifp->if_flags & IFF_LOOPBACK) == 0; + + IN_ADDRHASH_READER_FOREACH(ia, ip->ip_dst.s_addr) { + if (in_hosteq(ia->ia_addr.sin_addr, ip->ip_dst)) { + if (ia->ia4_flags & IN_IFF_NOTREADY) + continue; + if (checkif && ia->ia_ifp != ifp) + continue; + if ((ia->ia_ifp->if_flags & IFF_UP) == 0) { + (*downmatch)++; + continue; + } + if (ia->ia4_flags & IN_IFF_DETACHED && + (ifp->if_flags & IFF_LOOPBACK) == 0) + continue; + break; + } + } + + return ia; +} + +static struct in_ifaddr * +ip_match_our_address_broadcast(struct ifnet *ifp, struct ip *ip) +{ + struct in_ifaddr *ia = NULL; + struct ifaddr *ifa; + + IFADDR_READER_FOREACH(ifa, ifp) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + ia = ifatoia(ifa); + if (ia->ia4_flags & IN_IFF_NOTREADY) + continue; + if (ia->ia4_flags & IN_IFF_DETACHED && + (ifp->if_flags & IFF_LOOPBACK) == 0) + continue; + if (in_hosteq(ip->ip_dst, ia->ia_broadaddr.sin_addr) || + in_hosteq(ip->ip_dst, ia->ia_netbroadcast) || + /* + * Look for all-0's host part (old broadcast addr), + * either for subnet or net. + */ + ip->ip_dst.s_addr == ia->ia_subnet || + ip->ip_dst.s_addr == ia->ia_net) + goto matched; + /* + * An interface with IP address zero accepts + * all packets that arrive on that interface. + */ + if (in_nullhost(ia->ia_addr.sin_addr)) + goto matched; + } + ia = NULL; + +matched: + return ia; } -struct ip *ip_reass(); -struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; -struct route ipforward_rt; +/* + * IP software interrupt routine. + */ +static void +ipintr(void *arg __unused) +{ + struct mbuf *m; + + KASSERT(cpu_softintr_p()); + + SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); + while ((m = pktq_dequeue(ip_pktq)) != NULL) { + ip_input(m); + } + SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); +} /* - * Ip input routine. Checksum and byte swap header. If fragmented + * IP input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ -ipintr() +static void +ip_input(struct mbuf *m) { - register struct ip *ip; - register struct mbuf *m; - register struct ipq *fp; - register struct in_ifaddr *ia; - int hlen, s; - -next: - /* - * Get next datagram off input queue and get IP header - * in first mbuf. - */ - s = splimp(); - IF_DEQUEUE(&ipintrq, m); - splx(s); - if (m == 0) - return; -#ifdef DIAGNOSTIC - if ((m->m_flags & M_PKTHDR) == 0) - panic("ipintr no HDR"); -#endif + struct ip *ip = NULL; + struct in_ifaddr *ia = NULL; + int hlen = 0, len; + int downmatch; + int srcrt = 0; + ifnet_t *ifp; + struct psref psref; + int s; + + KASSERTMSG(cpu_softintr_p(), "ip_input: not in the software " + "interrupt handler; synchronization assumptions violated"); + + MCLAIM(m, &ip_rx_mowner); + KASSERT((m->m_flags & M_PKTHDR) != 0); + + ifp = m_get_rcvif_psref(m, &psref); + if (__predict_false(ifp == NULL)) + goto out; + /* * If no IP addresses have been set yet but the interfaces * are receiving, can't do anything with incoming packets yet. + * Note: we pre-check without locks held. */ - if (in_ifaddr == NULL) - goto bad; - ipstat.ips_total++; - if (m->m_len < sizeof (struct ip) && - (m = m_pullup(m, sizeof (struct ip))) == 0) { - ipstat.ips_toosmall++; - goto next; + if (IN_ADDRLIST_READER_EMPTY()) + goto out; + IP_STATINC(IP_STAT_TOTAL); + + /* + * If the IP header is not aligned, slurp it up into a new + * mbuf with space for link headers, in the event we forward + * it. Otherwise, if it is aligned, make sure the entire + * base IP header is in the first mbuf of the chain. + */ + if (IP_HDR_ALIGNED_P(mtod(m, void *)) == 0) { + if ((m = m_copyup(m, sizeof(struct ip), + (max_linkhdr + 3) & ~3)) == NULL) { + /* XXXJRT new stat, please */ + IP_STATINC(IP_STAT_TOOSMALL); + goto out; + } + } else if (__predict_false(m->m_len < sizeof (struct ip))) { + if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { + IP_STATINC(IP_STAT_TOOSMALL); + goto out; + } } ip = mtod(m, struct ip *); + if (ip->ip_v != IPVERSION) { + IP_STATINC(IP_STAT_BADVERS); + goto out; + } hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) { /* minimum header length */ - ipstat.ips_badhlen++; - goto bad; + IP_STATINC(IP_STAT_BADHLEN); + goto out; } if (hlen > m->m_len) { - if ((m = m_pullup(m, hlen)) == 0) { - ipstat.ips_badhlen++; - goto next; + if ((m = m_pullup(m, hlen)) == NULL) { + IP_STATINC(IP_STAT_BADHLEN); + goto out; } ip = mtod(m, struct ip *); } - if (ip->ip_sum = in_cksum(m, hlen)) { - ipstat.ips_badsum++; - goto bad; + + /* + * RFC1122: packets with a multicast source address are + * not allowed. + */ + if (IN_MULTICAST(ip->ip_src.s_addr)) { + IP_STATINC(IP_STAT_BADADDR); + goto out; + } + + /* 127/8 must not appear on wire - RFC1122 */ + if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || + (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { + if ((ifp->if_flags & IFF_LOOPBACK) == 0) { + IP_STATINC(IP_STAT_BADADDR); + goto out; + } + } + + switch (m->m_pkthdr.csum_flags & + ((ifp->if_csum_flags_rx & M_CSUM_IPv4) | + M_CSUM_IPv4_BAD)) { + case M_CSUM_IPv4|M_CSUM_IPv4_BAD: + INET_CSUM_COUNTER_INCR(&ip_hwcsum_bad); + IP_STATINC(IP_STAT_BADSUM); + goto out; + + case M_CSUM_IPv4: + /* Checksum was okay. */ + INET_CSUM_COUNTER_INCR(&ip_hwcsum_ok); + break; + + default: + /* + * Must compute it ourselves. Maybe skip checksum on + * loopback interfaces. + */ + if (__predict_true(!(ifp->if_flags & IFF_LOOPBACK) || + ip_do_loopback_cksum)) { + INET_CSUM_COUNTER_INCR(&ip_swcsum); + if (in_cksum(m, hlen) != 0) { + IP_STATINC(IP_STAT_BADSUM); + goto out; + } + } + break; } + /* Retrieve the packet length. */ + len = ntohs(ip->ip_len); + /* - * Convert fields to host representation. + * Check for additional length bogosity */ - NTOHS(ip->ip_len); - if (ip->ip_len < hlen) { - ipstat.ips_badlen++; - goto bad; + if (len < hlen) { + IP_STATINC(IP_STAT_BADLEN); + goto out; } - NTOHS(ip->ip_id); - NTOHS(ip->ip_off); /* * Check that the amount of data in the buffers @@ -203,17 +581,78 @@ next: * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ - if (m->m_pkthdr.len < ip->ip_len) { - ipstat.ips_tooshort++; - goto bad; + if (m->m_pkthdr.len < len) { + IP_STATINC(IP_STAT_TOOSHORT); + goto out; } - if (m->m_pkthdr.len > ip->ip_len) { + if (m->m_pkthdr.len > len) { if (m->m_len == m->m_pkthdr.len) { - m->m_len = ip->ip_len; - m->m_pkthdr.len = ip->ip_len; + m->m_len = len; + m->m_pkthdr.len = len; } else - m_adj(m, ip->ip_len - m->m_pkthdr.len); + m_adj(m, len - m->m_pkthdr.len); + } + + /* + * Assume that we can create a fast-forward IP flow entry + * based on this packet. + */ + m->m_flags |= M_CANFASTFWD; + + /* + * Run through list of hooks for input packets. If there are any + * filters which require that additional packets in the flow are + * not fast-forwarded, they must clear the M_CANFASTFWD flag. + * Note that filters must _never_ set this flag, as another filter + * in the list may have previously cleared it. + */ +#if defined(IPSEC) + if (!ipsec_used || !ipsec_indone(m)) +#else + if (1) +#endif + { + struct in_addr odst = ip->ip_dst; + bool freed; + + freed = pfil_run_hooks(inet_pfil_hook, &m, ifp, PFIL_IN) != 0; + if (freed || m == NULL) { + m = NULL; + goto out; + } + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + + /* + * XXX The setting of "srcrt" here is to prevent ip_forward() + * from generating ICMP redirects for packets that have + * been redirected by a hook back out on to the same LAN that + * they came from and is not an indication that the packet + * is being inffluenced by source routing options. This + * allows things like + * "rdr tlp0 0/0 port 80 -> 1.1.1.200 3128 tcp" + * where tlp0 is both on the 1.1.1.0/24 network and is the + * default route for hosts on 1.1.1.0/24. Of course this + * also requires a "map tlp0 ..." to complete the story. + * One might argue whether or not this kind of network config. + * should be supported in this manner... + */ + srcrt = (odst.s_addr != ip->ip_dst.s_addr); + } + +#ifdef ALTQ + /* XXX Temporary until ALTQ is changed to use a pfil hook */ + if (altq_input) { + SOFTNET_LOCK(); + if ((*altq_input)(m, AF_INET) == 0) { + /* Packet dropped by traffic conditioner. */ + SOFTNET_UNLOCK(); + m = NULL; + goto out; + } + SOFTNET_UNLOCK(); } +#endif /* * Process options and, if not destined for us, @@ -221,370 +660,236 @@ next: * error was detected (causing an icmp message * to be sent and the original packet to be freed). */ - ip_nhops = 0; /* for source routed packets */ - if (hlen > sizeof (struct ip) && ip_dooptions(m)) - goto next; + if (hlen > sizeof (struct ip) && ip_dooptions(m)) { + m = NULL; + goto out; + } /* * Check our list of addresses, to see if the packet is for us. - */ - for (ia = in_ifaddr; ia; ia = ia->ia_next) { -#define satosin(sa) ((struct sockaddr_in *)(sa)) + * + * Traditional 4.4BSD did not consult IFF_UP at all. + * The behavior here is to treat addresses on !IFF_UP interface + * or IN_IFF_NOTREADY addresses as not mine. + */ + downmatch = 0; + s = pserialize_read_enter(); + ia = ip_match_our_address(ifp, ip, &downmatch); + if (ia != NULL) { + pserialize_read_exit(s); + goto ours; + } - if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) + if (ifp->if_flags & IFF_BROADCAST) { + ia = ip_match_our_address_broadcast(ifp, ip); + if (ia != NULL) { + pserialize_read_exit(s); goto ours; - if ( -#ifdef DIRECTED_BROADCAST - ia->ia_ifp == m->m_pkthdr.rcvif && -#endif - (ia->ia_ifp->if_flags & IFF_BROADCAST)) { - u_long t; + } + } + pserialize_read_exit(s); - if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == - ip->ip_dst.s_addr) - goto ours; - if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr) - goto ours; + if (IN_MULTICAST(ip->ip_dst.s_addr)) { +#ifdef MROUTING + extern struct socket *ip_mrouter; + + if (ip_mrouter) { /* - * Look for all-0's host part (old broadcast addr), - * either for subnet or net. + * If we are acting as a multicast router, all + * incoming multicast packets are passed to the + * kernel-level multicast forwarding function. + * The packet is returned (relatively) intact; if + * ip_mforward() returns a non-zero value, the packet + * must be discarded, else it may be accepted below. + * + * (The IP ident field is put in the same byte order + * as expected when ip_mforward() is called from + * ip_output().) */ - t = ntohl(ip->ip_dst.s_addr); - if (t == ia->ia_subnet) - goto ours; - if (t == ia->ia_net) + SOFTNET_LOCK(); + if (ip_mforward(m, ifp) != 0) { + SOFTNET_UNLOCK(); + IP_STATINC(IP_STAT_CANTFORWARD); + goto out; + } + SOFTNET_UNLOCK(); + + /* + * The process-level routing demon needs to receive + * all multicast IGMP packets, whether or not this + * host belongs to their destination groups. + */ + if (ip->ip_p == IPPROTO_IGMP) { goto ours; + } + IP_STATINC(IP_STAT_CANTFORWARD); + } +#endif + /* + * See if we belong to the destination multicast group on the + * arrival interface. + */ + if (!in_multi_group(ip->ip_dst, ifp, 0)) { + IP_STATINC(IP_STAT_CANTFORWARD); + goto out; } - } - if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST) goto ours; - if (ip->ip_dst.s_addr == INADDR_ANY) + } + if (ip->ip_dst.s_addr == INADDR_BROADCAST || + in_nullhost(ip->ip_dst)) goto ours; /* * Not for us; forward if possible and desirable. */ if (ipforwarding == 0) { - ipstat.ips_cantforward++; + m_put_rcvif_psref(ifp, &psref); + IP_STATINC(IP_STAT_CANTFORWARD); m_freem(m); - } else - ip_forward(m, 0); - goto next; + } else { + /* + * If ip_dst matched any of my address on !IFF_UP interface, + * and there's no IFF_UP interface that matches ip_dst, + * send icmp unreach. Forwarding it will result in in-kernel + * forwarding loop till TTL goes to 0. + */ + if (downmatch) { + m_put_rcvif_psref(ifp, &psref); + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); + IP_STATINC(IP_STAT_CANTFORWARD); + return; + } +#ifdef IPSEC + /* Check the security policy (SP) for the packet */ + if (ipsec_used) { + if (ipsec4_input(m, IP_FORWARDING | + (ip_directedbcast ? IP_ALLOWBROADCAST : 0)) != 0) { + goto out; + } + } +#endif + ip_forward(m, srcrt, ifp); + m_put_rcvif_psref(ifp, &psref); + } + return; ours: + m_put_rcvif_psref(ifp, &psref); + ifp = NULL; + /* * If offset or IP_MF are set, must reassemble. - * Otherwise, nothing need be done. - * (We could look in the reassembly queue to see - * if the packet was previously fragmented, - * but it's not worth the time; just let them time out.) - */ - if (ip->ip_off &~ IP_DF) { - if (m->m_flags & M_EXT) { /* XXX */ - if ((m = m_pullup(m, sizeof (struct ip))) == 0) { - ipstat.ips_toosmall++; - goto next; - } - ip = mtod(m, struct ip *); - } - /* - * Look for queue of fragments - * of this datagram. - */ - for (fp = ipq.next; fp != &ipq; fp = fp->next) - if (ip->ip_id == fp->ipq_id && - ip->ip_src.s_addr == fp->ipq_src.s_addr && - ip->ip_dst.s_addr == fp->ipq_dst.s_addr && - ip->ip_p == fp->ipq_p) - goto found; - fp = 0; -found: - + */ + if (ip->ip_off & ~htons(IP_DF|IP_RF)) { /* - * Adjust ip_len to not reflect header, - * set ip_mff if more fragments are expected, - * convert offset of this to bytes. + * Pass to IP reassembly mechanism. */ - ip->ip_len -= hlen; - ((struct ipasfrag *)ip)->ipf_mff = 0; - if (ip->ip_off & IP_MF) - ((struct ipasfrag *)ip)->ipf_mff = 1; - ip->ip_off <<= 3; - + if (ip_reass_packet(&m, ip) != 0) { + /* Failed; invalid fragment(s) or packet. */ + goto out; + } + if (m == NULL) { + /* More fragments should come; silently return. */ + goto out; + } /* - * If datagram marked as having more fragments - * or if this is not the first fragment, - * attempt reassembly; if it succeeds, proceed. + * Reassembly is done, we have the final packet. + * Updated cached data in local variable(s). */ - if (((struct ipasfrag *)ip)->ipf_mff || ip->ip_off) { - ipstat.ips_fragments++; - ip = ip_reass((struct ipasfrag *)ip, fp); - if (ip == 0) - goto next; - else - ipstat.ips_reassembled++; - m = dtom(ip); - } else - if (fp) - ip_freef(fp); - } else - ip->ip_len -= hlen; - - /* - * Switch out to protocol's input routine. - */ - ipstat.ips_delivered++; - (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen); - goto next; -bad: - m_freem(m); - goto next; -} - -/* - * Take incoming datagram fragment and try to - * reassemble it into whole datagram. If a chain for - * reassembly of this datagram already exists, then it - * is given as fp; otherwise have to make a chain. - */ -struct ip * -ip_reass(ip, fp) - register struct ipasfrag *ip; - register struct ipq *fp; -{ - register struct mbuf *m = dtom(ip); - register struct ipasfrag *q; - struct mbuf *t; - int hlen = ip->ip_hl << 2; - int i, next; - - /* - * Presence of header sizes in mbufs - * would confuse code below. - */ - m->m_data += hlen; - m->m_len -= hlen; - - /* - * If first fragment to arrive, create a reassembly queue. - */ - if (fp == 0) { - if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL) - goto dropfrag; - fp = mtod(t, struct ipq *); - insque(fp, &ipq); - fp->ipq_ttl = IPFRAGTTL; - fp->ipq_p = ip->ip_p; - fp->ipq_id = ip->ip_id; - fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp; - fp->ipq_src = ((struct ip *)ip)->ip_src; - fp->ipq_dst = ((struct ip *)ip)->ip_dst; - q = (struct ipasfrag *)fp; - goto insert; + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; } +#ifdef IPSEC /* - * Find a segment which begins after this one does. + * Enforce IPsec policy checking if we are seeing last header. + * Note that we do not visit this with protocols with PCB layer + * code - like UDP/TCP/raw IP. */ - for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) - if (q->ip_off > ip->ip_off) - break; - - /* - * If there is a preceding segment, it may provide some of - * our data already. If so, drop the data from the incoming - * segment. If it provides all of our data, drop us. - */ - if (q->ipf_prev != (struct ipasfrag *)fp) { - i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off; - if (i > 0) { - if (i >= ip->ip_len) - goto dropfrag; - m_adj(dtom(ip), i); - ip->ip_off += i; - ip->ip_len -= i; + if (ipsec_used && + (inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) { + if (ipsec4_input(m, 0) != 0) { + goto out; } } +#endif /* - * While we overlap succeeding segments trim them or, - * if they are completely covered, dequeue them. - */ - while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) { - i = (ip->ip_off + ip->ip_len) - q->ip_off; - if (i < q->ip_len) { - q->ip_len -= i; - q->ip_off += i; - m_adj(dtom(q), i); - break; - } - q = q->ipf_next; - m_freem(dtom(q->ipf_prev)); - ip_deq(q->ipf_prev); - } - -insert: - /* - * Stick new segment in its place; - * check for complete reassembly. - */ - ip_enq(ip, q->ipf_prev); - next = 0; - for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) { - if (q->ip_off != next) - return (0); - next += q->ip_len; - } - if (q->ipf_prev->ipf_mff) - return (0); - - /* - * Reassembly is complete; concatenate fragments. - */ - q = fp->ipq_next; - m = dtom(q); - t = m->m_next; - m->m_next = 0; - m_cat(m, t); - q = q->ipf_next; - while (q != (struct ipasfrag *)fp) { - t = dtom(q); - q = q->ipf_next; - m_cat(m, t); + * Switch out to protocol's input routine. + */ +#if IFA_STATS + if (ia && ip) { + struct in_ifaddr *_ia; + /* + * Keep a reference from ip_match_our_address with psref + * is expensive, so explore ia here again. + */ + s = pserialize_read_enter(); + _ia = in_get_ia(ip->ip_dst); + _ia->ia_ifa.ifa_data.ifad_inbytes += ntohs(ip->ip_len); + pserialize_read_exit(s); } +#endif + IP_STATINC(IP_STAT_DELIVERED); - /* - * Create header for new ip packet by - * modifying header of first packet; - * dequeue and discard fragment reassembly header. - * Make header visible. - */ - ip = fp->ipq_next; - ip->ip_len = next; - ((struct ip *)ip)->ip_src = fp->ipq_src; - ((struct ip *)ip)->ip_dst = fp->ipq_dst; - remque(fp); - (void) m_free(dtom(fp)); - m = dtom(ip); - m->m_len += (ip->ip_hl << 2); - m->m_data -= (ip->ip_hl << 2); - /* some debugging cruft by sklower, below, will go away soon */ - if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */ - register int plen = 0; - for (t = m; m; m = m->m_next) - plen += m->m_len; - t->m_pkthdr.len = plen; - } - return ((struct ip *)ip); - -dropfrag: - ipstat.ips_fragdropped++; - m_freem(m); - return (0); -} - -/* - * Free a fragment reassembly header and all - * associated datagrams. - */ -ip_freef(fp) - struct ipq *fp; -{ - register struct ipasfrag *q, *p; - - for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) { - p = q->ipf_next; - ip_deq(q); - m_freem(dtom(q)); - } - remque(fp); - (void) m_free(dtom(fp)); -} + const int off = hlen, nh = ip->ip_p; -/* - * Put an ip fragment on a reassembly chain. - * Like insque, but pointers in middle of structure. - */ -ip_enq(p, prev) - register struct ipasfrag *p, *prev; -{ + (*inetsw[ip_protox[nh]].pr_input)(m, off, nh); + return; - p->ipf_prev = prev; - p->ipf_next = prev->ipf_next; - prev->ipf_next->ipf_prev = p; - prev->ipf_next = p; +out: + m_put_rcvif_psref(ifp, &psref); + if (m != NULL) + m_freem(m); } /* - * To ip_enq as remque is to insque. + * IP timer processing. */ -ip_deq(p) - register struct ipasfrag *p; +void +ip_slowtimo(void) { - p->ipf_prev->ipf_next = p->ipf_next; - p->ipf_next->ipf_prev = p->ipf_prev; -} + SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); -/* - * IP timer processing; - * if a timer expires on a reassembly - * queue, discard it. - */ -ip_slowtimo() -{ - register struct ipq *fp; - int s = splnet(); + ip_reass_slowtimo(); - fp = ipq.next; - if (fp == 0) { - splx(s); - return; - } - while (fp != &ipq) { - --fp->ipq_ttl; - fp = fp->next; - if (fp->prev->ipq_ttl == 0) { - ipstat.ips_fragtimeout++; - ip_freef(fp->prev); - } - } - splx(s); + SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); } /* - * Drain off all datagram fragments. + * IP drain processing. */ -ip_drain() +void +ip_drain(void) { - while (ipq.next != &ipq) { - ipstat.ips_fragdropped++; - ip_freef(ipq.next); - } + KERNEL_LOCK(1, NULL); + ip_reass_drain(); + KERNEL_UNLOCK_ONE(NULL); } -extern struct in_ifaddr *ifptoia(); -struct in_ifaddr *ip_rtaddr(); - /* - * Do option processing on a datagram, - * possibly discarding it if bad options are encountered, - * or forwarding it if source-routed. - * Returns 1 if packet has been forwarded/freed, - * 0 if the packet should be processed further. + * ip_dooptions: perform option processing on a datagram, possibly discarding + * it if bad options are encountered, or forwarding it if source-routed. + * + * => Returns true if packet has been forwarded/freed. + * => Returns false if the packet should be processed further. */ -ip_dooptions(m) - struct mbuf *m; +static bool +ip_dooptions(struct mbuf *m) { - register struct ip *ip = mtod(m, struct ip *); - register u_char *cp; - register struct ip_timestamp *ipt; - register struct in_ifaddr *ia; + struct ip *ip = mtod(m, struct ip *); + u_char *cp, *cp0; + struct ip_timestamp *ipt; + struct in_ifaddr *ia; int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; - struct in_addr *sin; + struct in_addr dst; n_time ntime; + struct ifaddr *ifa = NULL; + int s; + dst = ip->ip_dst; cp = (u_char *)(ip + 1); cnt = (ip->ip_hl << 2) - sizeof (struct ip); for (; cnt > 0; cnt -= optlen, cp += optlen) { @@ -594,8 +899,12 @@ ip_dooptions(m) if (opt == IPOPT_NOP) optlen = 1; else { + if (cnt < IPOPT_OLEN + sizeof(*cp)) { + code = &cp[IPOPT_OLEN] - (u_char *)ip; + goto bad; + } optlen = cp[IPOPT_OLEN]; - if (optlen <= 0 || optlen > cnt) { + if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } @@ -615,15 +924,32 @@ ip_dooptions(m) * address is on directly accessible net. */ case IPOPT_LSRR: - case IPOPT_SSRR: + case IPOPT_SSRR: { + struct psref psref; + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; + + if (ip_allowsrcrt == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_NET_PROHIB; + goto bad; + } + if (optlen < IPOPT_OFFSET + sizeof(*cp)) { + code = &cp[IPOPT_OLEN] - (u_char *)ip; + goto bad; + } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } ipaddr.sin_addr = ip->ip_dst; - ia = (struct in_ifaddr *) - ifa_ifwithaddr((struct sockaddr *)&ipaddr); - if (ia == 0) { + + s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sintosa(&ipaddr)); + if (ifa == NULL) { + pserialize_read_exit(s); if (opt == IPOPT_SSRR) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; @@ -635,39 +961,59 @@ ip_dooptions(m) */ break; } + pserialize_read_exit(s); + off--; /* 0 origin */ - if (off > optlen - sizeof(struct in_addr)) { + if ((off + sizeof(struct in_addr)) > optlen) { /* * End of source route. Should be for us. */ - save_rte(cp, ip->ip_src); + save_rte(m, cp, ip->ip_src); break; } /* * locate outgoing interface */ - bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr, + memcpy((void *)&ipaddr.sin_addr, (void *)(cp + off), sizeof(ipaddr.sin_addr)); if (opt == IPOPT_SSRR) { -#define INA struct in_ifaddr * -#define SA struct sockaddr * - if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0) - ia = in_iaonnetof(in_netof(ipaddr.sin_addr)); - } else - ia = ip_rtaddr(ipaddr.sin_addr); - if (ia == 0) { + ifa = ifa_ifwithladdr_psref(sintosa(&ipaddr), + &psref); + if (ifa != NULL) + ia = ifatoia(ifa); + else + ia = NULL; + } else { + ia = ip_rtaddr(ipaddr.sin_addr, &psref); + } + if (ia == NULL) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } ip->ip_dst = ipaddr.sin_addr; - bcopy((caddr_t)&(IA_SIN(ia)->sin_addr), - (caddr_t)(cp + off), sizeof(struct in_addr)); + bcopy((void *)&ia->ia_addr.sin_addr, + (void *)(cp + off), sizeof(struct in_addr)); + ia4_release(ia, &psref); cp[IPOPT_OFFSET] += sizeof(struct in_addr); - forward = 1; + /* + * Let ip_intr's mcast routing check handle mcast pkts + */ + forward = !IN_MULTICAST(ip->ip_dst.s_addr); break; + } + + case IPOPT_RR: { + struct psref psref; + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; - case IPOPT_RR: + if (optlen < IPOPT_OFFSET + sizeof(*cp)) { + code = &cp[IPOPT_OLEN] - (u_char *)ip; + goto bad; + } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; @@ -676,129 +1022,210 @@ ip_dooptions(m) * If no space remains, ignore. */ off--; /* 0 origin */ - if (off > optlen - sizeof(struct in_addr)) + if ((off + sizeof(struct in_addr)) > optlen) break; - bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr, + memcpy((void *)&ipaddr.sin_addr, (void *)(&ip->ip_dst), sizeof(ipaddr.sin_addr)); /* * locate outgoing interface; if we're the destination, * use the incoming interface (should be same). */ - if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 && - (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) { - type = ICMP_UNREACH; - code = ICMP_UNREACH_HOST; - goto bad; + ifa = ifa_ifwithaddr_psref(sintosa(&ipaddr), &psref); + if (ifa == NULL) { + ia = ip_rtaddr(ipaddr.sin_addr, &psref); + if (ia == NULL) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_HOST; + goto bad; + } + } else { + ia = ifatoia(ifa); } - bcopy((caddr_t)&(IA_SIN(ia)->sin_addr), - (caddr_t)(cp + off), sizeof(struct in_addr)); + bcopy((void *)&ia->ia_addr.sin_addr, + (void *)(cp + off), sizeof(struct in_addr)); + ia4_release(ia, &psref); cp[IPOPT_OFFSET] += sizeof(struct in_addr); break; + } case IPOPT_TS: code = cp - (u_char *)ip; ipt = (struct ip_timestamp *)cp; - if (ipt->ipt_len < 5) + if (ipt->ipt_len < 4 || ipt->ipt_len > 40) { + code = (u_char *)&ipt->ipt_len - (u_char *)ip; + goto bad; + } + if (ipt->ipt_ptr < 5) { + code = (u_char *)&ipt->ipt_ptr - (u_char *)ip; goto bad; - if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) { - if (++ipt->ipt_oflw == 0) + } + if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) { + if (++ipt->ipt_oflw == 0) { + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip; goto bad; + } break; } - sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); + cp0 = (cp + ipt->ipt_ptr - 1); switch (ipt->ipt_flg) { case IPOPT_TS_TSONLY: break; - case IPOPT_TS_TSANDADDR: - if (ipt->ipt_ptr + sizeof(n_time) + - sizeof(struct in_addr) > ipt->ipt_len) + case IPOPT_TS_TSANDADDR: { + struct ifnet *rcvif; + int _s, _ss; + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; + + if (ipt->ipt_ptr - 1 + sizeof(n_time) + + sizeof(struct in_addr) > ipt->ipt_len) { + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip; goto bad; - ia = ifptoia(m->m_pkthdr.rcvif); - bcopy((caddr_t)&IA_SIN(ia)->sin_addr, - (caddr_t)sin, sizeof(struct in_addr)); + } + ipaddr.sin_addr = dst; + _ss = pserialize_read_enter(); + rcvif = m_get_rcvif(m, &_s); + if (__predict_true(rcvif != NULL)) { + ifa = ifaof_ifpforaddr(sintosa(&ipaddr), + rcvif); + } + m_put_rcvif(rcvif, &_s); + if (ifa == NULL) { + pserialize_read_exit(_ss); + break; + } + ia = ifatoia(ifa); + bcopy(&ia->ia_addr.sin_addr, + cp0, sizeof(struct in_addr)); + pserialize_read_exit(_ss); ipt->ipt_ptr += sizeof(struct in_addr); break; + } - case IPOPT_TS_PRESPEC: - if (ipt->ipt_ptr + sizeof(n_time) + - sizeof(struct in_addr) > ipt->ipt_len) + case IPOPT_TS_PRESPEC: { + struct sockaddr_in ipaddr = { + .sin_len = sizeof(ipaddr), + .sin_family = AF_INET, + }; + + if (ipt->ipt_ptr - 1 + sizeof(n_time) + + sizeof(struct in_addr) > ipt->ipt_len) { + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip; goto bad; - bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr, + } + memcpy(&ipaddr.sin_addr, cp0, sizeof(struct in_addr)); - if (ifa_ifwithaddr((SA)&ipaddr) == 0) + s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sintosa(&ipaddr)); + if (ifa == NULL) { + pserialize_read_exit(s); continue; + } + pserialize_read_exit(s); ipt->ipt_ptr += sizeof(struct in_addr); break; + } default: + /* XXX can't take &ipt->ipt_flg */ + code = (u_char *)&ipt->ipt_ptr - + (u_char *)ip + 1; goto bad; } ntime = iptime(); - bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1, + cp0 = (u_char *) &ntime; /* XXX grumble, GCC... */ + memmove((char *)cp + ipt->ipt_ptr - 1, cp0, sizeof(n_time)); ipt->ipt_ptr += sizeof(n_time); } } if (forward) { - ip_forward(m, 1); - return (1); - } else - return (0); + struct ifnet *rcvif; + struct psref _psref; + + if (ip_forwsrcrt == 0) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_SRCFAIL; + goto bad; + } + + rcvif = m_get_rcvif_psref(m, &_psref); + if (__predict_false(rcvif == NULL)) { + type = ICMP_UNREACH; + code = ICMP_UNREACH_HOST; + goto bad; + } + ip_forward(m, 1, rcvif); + m_put_rcvif_psref(rcvif, &_psref); + return true; + } + return false; bad: - icmp_error(m, type, code); - return (1); + icmp_error(m, type, code, 0, 0); + IP_STATINC(IP_STAT_BADOPTIONS); + return true; } /* - * Given address of next destination (final or next hop), + * ip_rtaddr: given address of next destination (final or next hop), * return internet address info of interface to be used to get there. */ -struct in_ifaddr * -ip_rtaddr(dst) - struct in_addr dst; +static struct in_ifaddr * +ip_rtaddr(struct in_addr dst, struct psref *psref) { - register struct sockaddr_in *sin; + struct rtentry *rt; + union { + struct sockaddr dst; + struct sockaddr_in dst4; + } u; + struct route *ro; + + sockaddr_in_init(&u.dst4, &dst, 0); + + ro = percpu_getref(ipforward_rt_percpu); + rt = rtcache_lookup(ro, &u.dst); + if (rt == NULL) { + percpu_putref(ipforward_rt_percpu); + return NULL; + } + + ia4_acquire(ifatoia(rt->rt_ifa), psref); + rtcache_unref(rt, ro); + percpu_putref(ipforward_rt_percpu); - sin = (struct sockaddr_in *) &ipforward_rt.ro_dst; - - if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) { - if (ipforward_rt.ro_rt) { - RTFREE(ipforward_rt.ro_rt); - ipforward_rt.ro_rt = 0; - } - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - sin->sin_addr = dst; - - rtalloc(&ipforward_rt); - } - if (ipforward_rt.ro_rt == 0) - return ((struct in_ifaddr *)0); - return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa); + return ifatoia(rt->rt_ifa); } /* - * Save incoming source route for use in replies, - * to be picked up later by ip_srcroute if the receiver is interested. + * save_rte: save incoming source route for use in replies, to be picked + * up later by ip_srcroute if the receiver is interested. */ -save_rte(option, dst) - u_char *option; - struct in_addr dst; +static void +save_rte(struct mbuf *m, u_char *option, struct in_addr dst) { + struct ip_srcrt *isr; + struct m_tag *mtag; unsigned olen; olen = option[IPOPT_OLEN]; -#ifdef DIAGNOSTIC - if (ipprintfs) - printf("save_rte: olen %d\n", olen); -#endif - if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst))) + if (olen > sizeof(isr->isr_hdr) + sizeof(isr->isr_routes)) + return; + + mtag = m_tag_get(PACKET_TAG_SRCROUTE, sizeof(*isr), M_NOWAIT); + if (mtag == NULL) return; - bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen); - ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr); - ip_srcrt.dst = dst; + isr = (struct ip_srcrt *)(mtag + 1); + + memcpy(isr->isr_hdr, option, olen); + isr->isr_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr); + isr->isr_dst = dst; + m_tag_prepend(m, mtag); } /* @@ -807,103 +1234,88 @@ save_rte(option, dst) * The first hop is placed before the options, will be removed later. */ struct mbuf * -ip_srcroute() +ip_srcroute(struct mbuf *m0) { - register struct in_addr *p, *q; - register struct mbuf *m; + struct in_addr *p, *q; + struct mbuf *m; + struct ip_srcrt *isr; + struct m_tag *mtag; + + mtag = m_tag_find(m0, PACKET_TAG_SRCROUTE, NULL); + if (mtag == NULL) + return NULL; + isr = (struct ip_srcrt *)(mtag + 1); + + if (isr->isr_nhops == 0) + return NULL; - if (ip_nhops == 0) - return ((struct mbuf *)0); m = m_get(M_DONTWAIT, MT_SOOPTS); - if (m == 0) - return ((struct mbuf *)0); + if (m == NULL) + return NULL; -#define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) + MCLAIM(m, &inetdomain.dom_mowner); +#define OPTSIZ (sizeof(isr->isr_nop) + sizeof(isr->isr_hdr)) - /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ - m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) + - OPTSIZ; -#ifdef DIAGNOSTIC - if (ipprintfs) - printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len); -#endif + /* length is (nhops+1)*sizeof(addr) + sizeof(nop + header) */ + m->m_len = (isr->isr_nhops + 1) * sizeof(struct in_addr) + OPTSIZ; /* * First save first hop for return route */ - p = &ip_srcrt.route[ip_nhops - 1]; + p = &(isr->isr_routes[isr->isr_nhops - 1]); *(mtod(m, struct in_addr *)) = *p--; -#ifdef DIAGNOSTIC - if (ipprintfs) - printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr)); -#endif /* * Copy option fields and padding (nop) to mbuf. */ - ip_srcrt.nop = IPOPT_NOP; - ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF; - bcopy((caddr_t)&ip_srcrt.nop, - mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ); - q = (struct in_addr *)(mtod(m, caddr_t) + + isr->isr_nop = IPOPT_NOP; + isr->isr_hdr[IPOPT_OFFSET] = IPOPT_MINOFF; + memmove(mtod(m, char *) + sizeof(struct in_addr), &isr->isr_nop, + OPTSIZ); + q = (struct in_addr *)(mtod(m, char *) + sizeof(struct in_addr) + OPTSIZ); #undef OPTSIZ /* * Record return path as an IP source route, * reversing the path (pointers are now aligned). */ - while (p >= ip_srcrt.route) { -#ifdef DIAGNOSTIC - if (ipprintfs) - printf(" %lx", ntohl(q->s_addr)); -#endif + while (p >= isr->isr_routes) { *q++ = *p--; } /* * Last hop goes to final destination. */ - *q = ip_srcrt.dst; -#ifdef DIAGNOSTIC - if (ipprintfs) - printf(" %lx\n", ntohl(q->s_addr)); -#endif - return (m); + *q = isr->isr_dst; + m_tag_delete(m0, mtag); + return m; } -/* - * Strip out IP options, at higher - * level protocol in the kernel. - * Second argument is buffer to which options - * will be moved, and return value is their length. - * XXX should be deleted; last arg currently ignored. - */ -ip_stripoptions(m, mopt) - register struct mbuf *m; - struct mbuf *mopt; -{ - register int i; - struct ip *ip = mtod(m, struct ip *); - register caddr_t opts; - int olen; +const int inetctlerrmap[PRC_NCMDS] = { + [PRC_MSGSIZE] = EMSGSIZE, + [PRC_HOSTDEAD] = EHOSTDOWN, + [PRC_HOSTUNREACH] = EHOSTUNREACH, + [PRC_UNREACH_NET] = EHOSTUNREACH, + [PRC_UNREACH_HOST] = EHOSTUNREACH, + [PRC_UNREACH_PROTOCOL] = ECONNREFUSED, + [PRC_UNREACH_PORT] = ECONNREFUSED, + [PRC_UNREACH_SRCFAIL] = EHOSTUNREACH, + [PRC_PARAMPROB] = ENOPROTOOPT, +}; - olen = (ip->ip_hl<<2) - sizeof (struct ip); - opts = (caddr_t)(ip + 1); - i = m->m_len - (sizeof (struct ip) + olen); - bcopy(opts + olen, opts, (unsigned)i); - m->m_len -= olen; - if (m->m_flags & M_PKTHDR) - m->m_pkthdr.len -= olen; - ip->ip_hl = sizeof(struct ip) >> 2; +void +ip_fasttimo(void) +{ + if (ip_drainwanted) { + ip_drain(); + ip_drainwanted = 0; + } } -u_char inetctlerrmap[PRC_NCMDS] = { - 0, 0, 0, 0, - 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, - EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, - EMSGSIZE, EHOSTUNREACH, 0, 0, - 0, 0, 0, 0, - ENOPROTOOPT -}; +void +ip_drainstub(void) +{ + ip_drainwanted = 1; +} /* * Forward a packet. If some error occurs return the sender @@ -919,64 +1331,67 @@ u_char inetctlerrmap[PRC_NCMDS] = { * The srcrt parameter indicates whether the packet is being forwarded * via a source route. */ -ip_forward(m, srcrt) - struct mbuf *m; - int srcrt; +static void +ip_forward(struct mbuf *m, int srcrt, struct ifnet *rcvif) { - register struct ip *ip = mtod(m, struct ip *); - register struct sockaddr_in *sin; - register struct rtentry *rt; - int error, type = 0, code; + struct ip *ip = mtod(m, struct ip *); + struct rtentry *rt; + int error, type = 0, code = 0, destmtu = 0; struct mbuf *mcopy; - struct in_addr dest; + n_long dest; + union { + struct sockaddr dst; + struct sockaddr_in dst4; + } u; + uint64_t *ips; + struct route *ro; - dest.s_addr = 0; -#ifdef DIAGNOSTIC - if (ipprintfs) - printf("forward: src %x dst %x ttl %x\n", ip->ip_src, - ip->ip_dst, ip->ip_ttl); -#endif - if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) { - ipstat.ips_cantforward++; + KASSERTMSG(cpu_softintr_p(), "ip_forward: not in the software " + "interrupt handler; synchronization assumptions violated"); + + /* + * We are now in the output path. + */ + MCLAIM(m, &ip_tx_mowner); + + /* + * Clear any in-bound checksum flags for this packet. + */ + m->m_pkthdr.csum_flags = 0; + + dest = 0; + if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) { + IP_STATINC(IP_STAT_CANTFORWARD); m_freem(m); return; } - HTONS(ip->ip_id); + if (ip->ip_ttl <= IPTTLDEC) { - icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest); + icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0); return; } - ip->ip_ttl -= IPTTLDEC; - sin = (struct sockaddr_in *)&ipforward_rt.ro_dst; - if ((rt = ipforward_rt.ro_rt) == 0 || - ip->ip_dst.s_addr != sin->sin_addr.s_addr) { - if (ipforward_rt.ro_rt) { - RTFREE(ipforward_rt.ro_rt); - ipforward_rt.ro_rt = 0; - } - sin->sin_family = AF_INET; - sin->sin_len = sizeof(*sin); - sin->sin_addr = ip->ip_dst; - - rtalloc(&ipforward_rt); - if (ipforward_rt.ro_rt == 0) { - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest); - return; - } - rt = ipforward_rt.ro_rt; + sockaddr_in_init(&u.dst4, &ip->ip_dst, 0); + + ro = percpu_getref(ipforward_rt_percpu); + rt = rtcache_lookup(ro, &u.dst); + if (rt == NULL) { + percpu_putref(ipforward_rt_percpu); + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, dest, 0); + return; } /* - * Save at most 64 bytes of the packet in case + * Save at most 68 bytes of the packet in case * we need to generate an ICMP message to the src. + * Pullup to avoid sharing mbuf cluster between m and mcopy. */ - mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64)); + mcopy = m_copym(m, 0, imin(ntohs(ip->ip_len), 68), M_DONTWAIT); + if (mcopy) + mcopy = m_pullup(mcopy, ip->ip_hl << 2); + + ip->ip_ttl -= IPTTLDEC; -#ifdef GATEWAY - ip_ifmatrix[rt->rt_ifp->if_index + - if_index * m->m_pkthdr.rcvif->if_index]++; -#endif /* * If forwarding packet using same interface that it came in on, * perhaps should send a redirect to sender to shortcut a hop. @@ -985,59 +1400,64 @@ ip_forward(m, srcrt) * Also, don't send redirect if forwarding using a default route * or a route modified by a redirect. */ -#define satosin(sa) ((struct sockaddr_in *)(sa)) - if (rt->rt_ifp == m->m_pkthdr.rcvif && + if (rt->rt_ifp == rcvif && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 && - satosin(rt_key(rt))->sin_addr.s_addr != 0 && + !in_nullhost(satocsin(rt_getkey(rt))->sin_addr) && ipsendredirects && !srcrt) { - struct in_ifaddr *ia; - u_long src = ntohl(ip->ip_src.s_addr); - u_long dst = ntohl(ip->ip_dst.s_addr); - - if ((ia = ifptoia(m->m_pkthdr.rcvif)) && - (src & ia->ia_subnetmask) == ia->ia_subnet) { - if (rt->rt_flags & RTF_GATEWAY) - dest = satosin(rt->rt_gateway)->sin_addr; - else - dest = ip->ip_dst; - /* - * If the destination is reached by a route to host, - * is on a subnet of a local net, or is directly - * on the attached net (!), use host redirect. - * (We may be the correct first hop for other subnets.) - */ -#define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa)) - type = ICMP_REDIRECT; - if ((rt->rt_flags & RTF_HOST) || - (rt->rt_flags & RTF_GATEWAY) == 0) - code = ICMP_REDIRECT_HOST; - else if (RTA(rt)->ia_subnetmask != RTA(rt)->ia_netmask && - (dst & RTA(rt)->ia_netmask) == RTA(rt)->ia_net) - code = ICMP_REDIRECT_HOST; - else - code = ICMP_REDIRECT_NET; -#ifdef DIAGNOSTIC - if (ipprintfs) - printf("redirect (%d) to %x\n", code, dest.s_addr); -#endif + if (rt->rt_ifa && + (ip->ip_src.s_addr & ifatoia(rt->rt_ifa)->ia_subnetmask) == + ifatoia(rt->rt_ifa)->ia_subnet) { + if (rt->rt_flags & RTF_GATEWAY) + dest = satosin(rt->rt_gateway)->sin_addr.s_addr; + else + dest = ip->ip_dst.s_addr; + /* + * Router requirements says to only send host + * redirects. + */ + type = ICMP_REDIRECT; + code = ICMP_REDIRECT_HOST; } } + rtcache_unref(rt, ro); - error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING); - if (error) - ipstat.ips_cantforward++; - else { - ipstat.ips_forward++; - if (type) - ipstat.ips_redirectsent++; - else { - if (mcopy) - m_freem(mcopy); - return; - } + error = ip_output(m, NULL, ro, + (IP_FORWARDING | (ip_directedbcast ? IP_ALLOWBROADCAST : 0)), + NULL, NULL); + + if (error) { + IP_STATINC(IP_STAT_CANTFORWARD); + goto error; } - if (mcopy == NULL) + + ips = IP_STAT_GETREF(); + ips[IP_STAT_FORWARD]++; + + if (type) { + ips[IP_STAT_REDIRECTSENT]++; + IP_STAT_PUTREF(); + goto redirect; + } + + IP_STAT_PUTREF(); + if (mcopy) { +#ifdef GATEWAY + if (mcopy->m_flags & M_CANFASTFWD) + ipflow_create(ro, mcopy); +#endif + m_freem(mcopy); + } + + percpu_putref(ipforward_rt_percpu); + return; + +redirect: +error: + if (mcopy == NULL) { + percpu_putref(ipforward_rt_percpu); return; + } + switch (error) { case 0: /* forwarded, but need redirect */ @@ -1056,13 +1476,361 @@ ip_forward(m, srcrt) case EMSGSIZE: type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG; - ipstat.ips_cantfrag++; + + if ((rt = rtcache_validate(ro)) != NULL) { + destmtu = rt->rt_ifp->if_mtu; + rtcache_unref(rt, ro); + } +#ifdef IPSEC + if (ipsec_used) + (void)ipsec4_forward(mcopy, &destmtu); +#endif + IP_STATINC(IP_STAT_CANTFRAG); break; case ENOBUFS: - type = ICMP_SOURCEQUENCH; - code = 0; - break; + /* + * Do not generate ICMP_SOURCEQUENCH as required in RFC 1812, + * Requirements for IP Version 4 Routers. Source quench can + * big problem under DoS attacks or if the underlying + * interface is rate-limited. + */ + if (mcopy) + m_freem(mcopy); + percpu_putref(ipforward_rt_percpu); + return; + } + icmp_error(mcopy, type, code, dest, destmtu); + percpu_putref(ipforward_rt_percpu); +} + +void +ip_savecontrol(struct inpcb *inp, struct mbuf **mp, struct ip *ip, + struct mbuf *m) +{ + struct socket *so = inp->inp_socket; + ifnet_t *ifp; + int inpflags = inp->inp_flags; + struct psref psref; + + ifp = m_get_rcvif_psref(m, &psref); + if (__predict_false(ifp == NULL)) + return; /* XXX should report error? */ + + if (so->so_options & SO_TIMESTAMP +#ifdef SO_OTIMESTAMP + || so->so_options & SO_OTIMESTAMP +#endif + ) { + struct timeval tv; + + microtime(&tv); +#ifdef SO_OTIMESTAMP + if (so->so_options & SO_OTIMESTAMP) { + struct timeval50 tv50; + timeval_to_timeval50(&tv, &tv50); + *mp = sbcreatecontrol((void *) &tv50, sizeof(tv50), + SCM_OTIMESTAMP, SOL_SOCKET); + } else +#endif + *mp = sbcreatecontrol((void *) &tv, sizeof(tv), + SCM_TIMESTAMP, SOL_SOCKET); + if (*mp) + mp = &(*mp)->m_next; + } + if (inpflags & INP_RECVDSTADDR) { + *mp = sbcreatecontrol((void *) &ip->ip_dst, + sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } + if (inpflags & INP_RECVPKTINFO) { + struct in_pktinfo ipi; + ipi.ipi_addr = ip->ip_dst; + ipi.ipi_ifindex = ifp->if_index; + *mp = sbcreatecontrol((void *) &ipi, + sizeof(ipi), IP_PKTINFO, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } + if (inpflags & INP_RECVIF) { + struct sockaddr_dl sdl; + + sockaddr_dl_init(&sdl, sizeof(sdl), ifp->if_index, 0, NULL, 0, + NULL, 0); + *mp = sbcreatecontrol(&sdl, sdl.sdl_len, IP_RECVIF, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; + } + if (inpflags & INP_RECVTTL) { + *mp = sbcreatecontrol((void *) &ip->ip_ttl, + sizeof(uint8_t), IP_RECVTTL, IPPROTO_IP); + if (*mp) + mp = &(*mp)->m_next; } - icmp_error(mcopy, type, code, dest); + m_put_rcvif_psref(ifp, &psref); +} + +/* + * sysctl helper routine for net.inet.ip.forwsrcrt. + */ +static int +sysctl_net_inet_ip_forwsrcrt(SYSCTLFN_ARGS) +{ + int error, tmp; + struct sysctlnode node; + + node = *rnode; + tmp = ip_forwsrcrt; + node.sysctl_data = &tmp; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + error = kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FORWSRCRT, + 0, NULL, NULL, NULL); + if (error) + return (error); + + ip_forwsrcrt = tmp; + + return (0); +} + +/* + * sysctl helper routine for net.inet.ip.mtudisctimeout. checks the + * range of the new value and tweaks timers if it changes. + */ +static int +sysctl_net_inet_ip_pmtudto(SYSCTLFN_ARGS) +{ + int error, tmp; + struct sysctlnode node; + + icmp_mtudisc_lock(); + + node = *rnode; + tmp = ip_mtudisc_timeout; + node.sysctl_data = &tmp; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + goto out; + if (tmp < 0) { + error = EINVAL; + goto out; + } + + ip_mtudisc_timeout = tmp; + rt_timer_queue_change(ip_mtudisc_timeout_q, ip_mtudisc_timeout); + error = 0; +out: + icmp_mtudisc_unlock(); + return error; +} + +static int +sysctl_net_inet_ip_stats(SYSCTLFN_ARGS) +{ + + return (NETSTAT_SYSCTL(ipstat_percpu, IP_NSTATS)); +} + +static void +sysctl_net_inet_ip_setup(struct sysctllog **clog) +{ + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "inet", + SYSCTL_DESCR("PF_INET related settings"), + NULL, 0, NULL, 0, + CTL_NET, PF_INET, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "ip", + SYSCTL_DESCR("IPv4 related settings"), + NULL, 0, NULL, 0, + CTL_NET, PF_INET, IPPROTO_IP, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "forwarding", + SYSCTL_DESCR("Enable forwarding of INET datagrams"), + NULL, 0, &ipforwarding, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_FORWARDING, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "redirect", + SYSCTL_DESCR("Enable sending of ICMP redirect messages"), + NULL, 0, &ipsendredirects, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_SENDREDIRECTS, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "ttl", + SYSCTL_DESCR("Default TTL for an INET datagram"), + NULL, 0, &ip_defttl, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_DEFTTL, CTL_EOL); +#ifdef IPCTL_DEFMTU + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT /* |CTLFLAG_READWRITE? */, + CTLTYPE_INT, "mtu", + SYSCTL_DESCR("Default MTA for an INET route"), + NULL, 0, &ip_mtu, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_DEFMTU, CTL_EOL); +#endif /* IPCTL_DEFMTU */ + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "forwsrcrt", + SYSCTL_DESCR("Enable forwarding of source-routed " + "datagrams"), + sysctl_net_inet_ip_forwsrcrt, 0, &ip_forwsrcrt, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_FORWSRCRT, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "directed-broadcast", + SYSCTL_DESCR("Enable forwarding of broadcast datagrams"), + NULL, 0, &ip_directedbcast, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_DIRECTEDBCAST, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "allowsrcrt", + SYSCTL_DESCR("Accept source-routed datagrams"), + NULL, 0, &ip_allowsrcrt, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_ALLOWSRCRT, CTL_EOL); + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "mtudisc", + SYSCTL_DESCR("Use RFC1191 Path MTU Discovery"), + NULL, 0, &ip_mtudisc, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_MTUDISC, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "anonportmin", + SYSCTL_DESCR("Lowest ephemeral port number to assign"), + sysctl_net_inet_ip_ports, 0, &anonportmin, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_ANONPORTMIN, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "anonportmax", + SYSCTL_DESCR("Highest ephemeral port number to assign"), + sysctl_net_inet_ip_ports, 0, &anonportmax, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_ANONPORTMAX, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "mtudisctimeout", + SYSCTL_DESCR("Lifetime of a Path MTU Discovered route"), + sysctl_net_inet_ip_pmtudto, 0, (void *)&ip_mtudisc_timeout, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_MTUDISCTIMEOUT, CTL_EOL); +#ifndef IPNOPRIVPORTS + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "lowportmin", + SYSCTL_DESCR("Lowest privileged ephemeral port number " + "to assign"), + sysctl_net_inet_ip_ports, 0, &lowportmin, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_LOWPORTMIN, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "lowportmax", + SYSCTL_DESCR("Highest privileged ephemeral port number " + "to assign"), + sysctl_net_inet_ip_ports, 0, &lowportmax, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_LOWPORTMAX, CTL_EOL); +#endif /* IPNOPRIVPORTS */ +#if NGRE > 0 + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "grettl", + SYSCTL_DESCR("Default TTL for a gre tunnel datagram"), + NULL, 0, &ip_gre_ttl, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_GRE_TTL, CTL_EOL); +#endif /* NGRE */ + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "checkinterface", + SYSCTL_DESCR("Enable receive side of Strong ES model " + "from RFC1122"), + NULL, 0, &ip_checkinterface, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_CHECKINTERFACE, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "random_id", + SYSCTL_DESCR("Assign random ip_id values"), + NULL, 0, &ip_do_randomid, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_RANDOMID, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "do_loopback_cksum", + SYSCTL_DESCR("Perform IP checksum on loopback"), + NULL, 0, &ip_do_loopback_cksum, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_LOOPBACKCKSUM, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_STRUCT, "stats", + SYSCTL_DESCR("IP statistics"), + sysctl_net_inet_ip_stats, 0, NULL, 0, + CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS, + CTL_EOL); +#if NARP + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "dad_count", + SYSCTL_DESCR("Number of Duplicate Address Detection " + "probes to send"), + NULL, 0, &ip_dad_count, 0, + CTL_NET, PF_INET, IPPROTO_IP, + IPCTL_DAD_COUNT, CTL_EOL); +#endif + + /* anonportalgo RFC6056 subtree */ + const struct sysctlnode *portalgo_node; + sysctl_createv(clog, 0, NULL, &portalgo_node, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "anonportalgo", + SYSCTL_DESCR("Anonymous Port Algorithm Selection (RFC 6056)"), + NULL, 0, NULL, 0, + CTL_NET, PF_INET, IPPROTO_IP, CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &portalgo_node, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_STRING, "available", + SYSCTL_DESCR("available algorithms"), + sysctl_portalgo_available, 0, NULL, PORTALGO_MAXLEN, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &portalgo_node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_STRING, "selected", + SYSCTL_DESCR("selected algorithm"), + sysctl_portalgo_selected4, 0, NULL, PORTALGO_MAXLEN, + CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, &portalgo_node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_STRUCT, "reserve", + SYSCTL_DESCR("bitmap of reserved ports"), + sysctl_portalgo_reserve4, 0, NULL, 0, + CTL_CREATE, CTL_EOL); +} + +void +ip_statinc(u_int stat) +{ + + KASSERT(stat < IP_NSTATS); + IP_STATINC(stat); }