xref: /freebsd/sys/netinet/ip_input.c (revision c398230b64aea809cb7c5cea8db580af7097920c)
1c398230bSWarner Losh /*-
2df8bae1dSRodney W. Grimes  * Copyright (c) 1982, 1986, 1988, 1993
3df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
6df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
7df8bae1dSRodney W. Grimes  * are met:
8df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
9df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
10df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
11df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
12df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
13df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
14df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
15df8bae1dSRodney W. Grimes  *    without specific prior written permission.
16df8bae1dSRodney W. Grimes  *
17df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
28df8bae1dSRodney W. Grimes  *
29df8bae1dSRodney W. Grimes  *	@(#)ip_input.c	8.2 (Berkeley) 1/4/94
30c3aac50fSPeter Wemm  * $FreeBSD$
31df8bae1dSRodney W. Grimes  */
32df8bae1dSRodney W. Grimes 
330ac40133SBrian Somers #include "opt_bootp.h"
3474a9466cSGary Palmer #include "opt_ipfw.h"
3527108a15SDag-Erling Smørgrav #include "opt_ipstealth.h"
366a800098SYoshinobu Inoue #include "opt_ipsec.h"
3736b0360bSRobert Watson #include "opt_mac.h"
3874a9466cSGary Palmer 
39df8bae1dSRodney W. Grimes #include <sys/param.h>
40df8bae1dSRodney W. Grimes #include <sys/systm.h>
415f311da2SMike Silbersack #include <sys/callout.h>
4236b0360bSRobert Watson #include <sys/mac.h>
43df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
44b715f178SLuigi Rizzo #include <sys/malloc.h>
45df8bae1dSRodney W. Grimes #include <sys/domain.h>
46df8bae1dSRodney W. Grimes #include <sys/protosw.h>
47df8bae1dSRodney W. Grimes #include <sys/socket.h>
48df8bae1dSRodney W. Grimes #include <sys/time.h>
49df8bae1dSRodney W. Grimes #include <sys/kernel.h>
501025071fSGarrett Wollman #include <sys/syslog.h>
51b5e8ce9fSBruce Evans #include <sys/sysctl.h>
52df8bae1dSRodney W. Grimes 
53c85540ddSAndrey A. Chernov #include <net/pfil.h>
54df8bae1dSRodney W. Grimes #include <net/if.h>
559494d596SBrooks Davis #include <net/if_types.h>
56d314ad7bSJulian Elischer #include <net/if_var.h>
5782c23ebaSBill Fenner #include <net/if_dl.h>
58df8bae1dSRodney W. Grimes #include <net/route.h>
59748e0b0aSGarrett Wollman #include <net/netisr.h>
60df8bae1dSRodney W. Grimes 
61df8bae1dSRodney W. Grimes #include <netinet/in.h>
62df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
63b5e8ce9fSBruce Evans #include <netinet/in_var.h>
64df8bae1dSRodney W. Grimes #include <netinet/ip.h>
65df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
66df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
67df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
6858938916SGarrett Wollman #include <machine/in_cksum.h>
69df8bae1dSRodney W. Grimes 
70f0068c4aSGarrett Wollman #include <sys/socketvar.h>
716ddbf1e2SGary Palmer 
729b932e9eSAndre Oppermann /* XXX: Temporary until ipfw_ether and ipfw_bridge are converted. */
736ddbf1e2SGary Palmer #include <netinet/ip_fw.h>
74db69a05dSPaul Saab #include <netinet/ip_dummynet.h>
75db69a05dSPaul Saab 
766a800098SYoshinobu Inoue #ifdef IPSEC
776a800098SYoshinobu Inoue #include <netinet6/ipsec.h>
786a800098SYoshinobu Inoue #include <netkey/key.h>
796a800098SYoshinobu Inoue #endif
806a800098SYoshinobu Inoue 
81b9234fafSSam Leffler #ifdef FAST_IPSEC
82b9234fafSSam Leffler #include <netipsec/ipsec.h>
83b9234fafSSam Leffler #include <netipsec/key.h>
84b9234fafSSam Leffler #endif
85b9234fafSSam Leffler 
861c5de19aSGarrett Wollman int rsvp_on = 0;
87f0068c4aSGarrett Wollman 
881f91d8c5SDavid Greenman int	ipforwarding = 0;
890312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW,
903d177f46SBill Fumerola     &ipforwarding, 0, "Enable IP forwarding between interfaces");
910312fbe9SPoul-Henning Kamp 
92d4fb926cSGarrett Wollman static int	ipsendredirects = 1; /* XXX */
930312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW,
943d177f46SBill Fumerola     &ipsendredirects, 0, "Enable sending IP redirects");
950312fbe9SPoul-Henning Kamp 
96df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
970312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW,
983d177f46SBill Fumerola     &ip_defttl, 0, "Maximum TTL on IP packets");
990312fbe9SPoul-Henning Kamp 
1000312fbe9SPoul-Henning Kamp static int	ip_dosourceroute = 0;
1010312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
1023d177f46SBill Fumerola     &ip_dosourceroute, 0, "Enable forwarding source routed IP packets");
1034fce5804SGuido van Rooij 
1044fce5804SGuido van Rooij static int	ip_acceptsourceroute = 0;
1054fce5804SGuido van Rooij SYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute,
1063d177f46SBill Fumerola     CTLFLAG_RW, &ip_acceptsourceroute, 0,
1073d177f46SBill Fumerola     "Enable accepting source routed IP packets");
1086a800098SYoshinobu Inoue 
1092bde81acSAndre Oppermann int		ip_doopts = 1;	/* 0 = ignore, 1 = process, 2 = reject */
1102bde81acSAndre Oppermann SYSCTL_INT(_net_inet_ip, OID_AUTO, process_options, CTLFLAG_RW,
1112bde81acSAndre Oppermann     &ip_doopts, 0, "Enable IP options processing ([LS]SRR, RR, TS)");
1122bde81acSAndre Oppermann 
1136a800098SYoshinobu Inoue static int	ip_keepfaith = 0;
1146a800098SYoshinobu Inoue SYSCTL_INT(_net_inet_ip, IPCTL_KEEPFAITH, keepfaith, CTLFLAG_RW,
1156a800098SYoshinobu Inoue 	&ip_keepfaith,	0,
1166a800098SYoshinobu Inoue 	"Enable packet capture for FAITH IPv4->IPv6 translater daemon");
1176a800098SYoshinobu Inoue 
118402062e8SMike Silbersack static int    nipq = 0;         /* total # of reass queues */
119402062e8SMike Silbersack static int    maxnipq;
120690a6055SJesper Skriver SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfragpackets, CTLFLAG_RW,
121402062e8SMike Silbersack 	&maxnipq, 0,
122690a6055SJesper Skriver 	"Maximum number of IPv4 fragment reassembly queue entries");
123690a6055SJesper Skriver 
124375386e2SMike Silbersack static int    maxfragsperpacket;
125375386e2SMike Silbersack SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfragsperpacket, CTLFLAG_RW,
126375386e2SMike Silbersack 	&maxfragsperpacket, 0,
127375386e2SMike Silbersack 	"Maximum number of IPv4 fragments allowed per packet");
128375386e2SMike Silbersack 
129df285b3dSMike Silbersack static int	ip_sendsourcequench = 0;
130df285b3dSMike Silbersack SYSCTL_INT(_net_inet_ip, OID_AUTO, sendsourcequench, CTLFLAG_RW,
131df285b3dSMike Silbersack 	&ip_sendsourcequench, 0,
132df285b3dSMike Silbersack 	"Enable the transmission of source quench packets");
133df285b3dSMike Silbersack 
1341f44b0a1SDavid Malone int	ip_do_randomid = 0;
1351f44b0a1SDavid Malone SYSCTL_INT(_net_inet_ip, OID_AUTO, random_id, CTLFLAG_RW,
1361f44b0a1SDavid Malone 	&ip_do_randomid, 0,
1371f44b0a1SDavid Malone 	"Assign random ip_id values");
1381f44b0a1SDavid Malone 
139823db0e9SDon Lewis /*
140823db0e9SDon Lewis  * XXX - Setting ip_checkinterface mostly implements the receive side of
141823db0e9SDon Lewis  * the Strong ES model described in RFC 1122, but since the routing table
142a8f12100SDon Lewis  * and transmit implementation do not implement the Strong ES model,
143823db0e9SDon Lewis  * setting this to 1 results in an odd hybrid.
1443f67c834SDon Lewis  *
145a8f12100SDon Lewis  * XXX - ip_checkinterface currently must be disabled if you use ipnat
146a8f12100SDon Lewis  * to translate the destination address to another local interface.
1473f67c834SDon Lewis  *
1483f67c834SDon Lewis  * XXX - ip_checkinterface must be disabled if you add IP aliases
1493f67c834SDon Lewis  * to the loopback interface instead of the interface where the
1503f67c834SDon Lewis  * packets for those addresses are received.
151823db0e9SDon Lewis  */
1524bc37f98SMaxim Konovalov static int	ip_checkinterface = 0;
153b3e95d4eSJonathan Lemon SYSCTL_INT(_net_inet_ip, OID_AUTO, check_interface, CTLFLAG_RW,
154b3e95d4eSJonathan Lemon     &ip_checkinterface, 0, "Verify packet arrives on correct interface");
155b3e95d4eSJonathan Lemon 
156df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1570312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
158df8bae1dSRodney W. Grimes #endif
159c21fd232SAndre Oppermann 
160c21fd232SAndre Oppermann struct pfil_head inet_pfil_hook;	/* Packet filter hooks */
161df8bae1dSRodney W. Grimes 
1621cafed39SJonathan Lemon static struct	ifqueue ipintrq;
163ca925d9cSJonathan Lemon static int	ipqmaxlen = IFQ_MAXLEN;
164ca925d9cSJonathan Lemon 
165df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
166f0ffb944SJulian Elischer extern	struct protosw inetsw[];
167df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
16859562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; 		/* first inet address */
169ca925d9cSJonathan Lemon struct	in_ifaddrhashhead *in_ifaddrhashtbl;	/* inet addr hash table  */
170ca925d9cSJonathan Lemon u_long 	in_ifaddrhmask;				/* mask for hash table */
171ca925d9cSJonathan Lemon 
172afed1375SDavid Greenman SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RW,
1733d177f46SBill Fumerola     &ipintrq.ifq_maxlen, 0, "Maximum size of the IP input queue");
1740312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1753d177f46SBill Fumerola     &ipintrq.ifq_drops, 0, "Number of packets dropped from the IP input queue");
176df8bae1dSRodney W. Grimes 
177f23b4c91SGarrett Wollman struct ipstat ipstat;
178c73d99b5SRuslan Ermilov SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RW,
1793d177f46SBill Fumerola     &ipstat, ipstat, "IP statistics (struct ipstat, netinet/ip_var.h)");
180194a213eSAndrey A. Chernov 
181194a213eSAndrey A. Chernov /* Packet reassembly stuff */
182194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
183194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
184194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
185194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
186831a80b0SMatthew Dillon 	(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
187194a213eSAndrey A. Chernov 
188462b86feSPoul-Henning Kamp static TAILQ_HEAD(ipqhead, ipq) ipq[IPREASS_NHASH];
1892fad1e93SSam Leffler struct mtx ipqlock;
1905f311da2SMike Silbersack struct callout ipport_tick_callout;
1912fad1e93SSam Leffler 
1922fad1e93SSam Leffler #define	IPQ_LOCK()	mtx_lock(&ipqlock)
1932fad1e93SSam Leffler #define	IPQ_UNLOCK()	mtx_unlock(&ipqlock)
194888c2a3cSSam Leffler #define	IPQ_LOCK_INIT()	mtx_init(&ipqlock, "ipqlock", NULL, MTX_DEF)
195888c2a3cSSam Leffler #define	IPQ_LOCK_ASSERT()	mtx_assert(&ipqlock, MA_OWNED)
196f23b4c91SGarrett Wollman 
1970312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1980312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1993d177f46SBill Fumerola     &ip_mtu, 0, "Default MTU");
2000312fbe9SPoul-Henning Kamp #endif
2010312fbe9SPoul-Henning Kamp 
2021b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
203c76ff708SAndre Oppermann int	ipstealth = 0;
2041b968362SDag-Erling Smørgrav SYSCTL_INT(_net_inet_ip, OID_AUTO, stealth, CTLFLAG_RW,
2051b968362SDag-Erling Smørgrav     &ipstealth, 0, "");
2061b968362SDag-Erling Smørgrav #endif
2071b968362SDag-Erling Smørgrav 
2089b932e9eSAndre Oppermann /*
2099b932e9eSAndre Oppermann  * ipfw_ether and ipfw_bridge hooks.
2109b932e9eSAndre Oppermann  * XXX: Temporary until those are converted to pfil_hooks as well.
2119b932e9eSAndre Oppermann  */
2129b932e9eSAndre Oppermann ip_fw_chk_t *ip_fw_chk_ptr = NULL;
2139b932e9eSAndre Oppermann ip_dn_io_t *ip_dn_io_ptr = NULL;
214e4c97effSAndre Oppermann int fw_enable = 1;
21597850a5dSLuigi Rizzo int fw_one_pass = 1;
216e7319babSPoul-Henning Kamp 
217929b31ddSSam Leffler /*
218e0982661SAndre Oppermann  * XXX this is ugly.  IP options source routing magic.
219df8bae1dSRodney W. Grimes  */
220e0982661SAndre Oppermann struct ipoptrt {
221df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
222df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
223df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
224df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
225e0982661SAndre Oppermann };
226df8bae1dSRodney W. Grimes 
227e0982661SAndre Oppermann struct ipopt_tag {
228e0982661SAndre Oppermann 	struct	m_tag tag;
229e0982661SAndre Oppermann 	int	ip_nhops;
230e0982661SAndre Oppermann 	struct	ipoptrt ip_srcrt;
231e0982661SAndre Oppermann };
232e0982661SAndre Oppermann 
233e0982661SAndre Oppermann static void	save_rte(struct mbuf *, u_char *, struct in_addr);
2349b932e9eSAndre Oppermann static int	ip_dooptions(struct mbuf *m, int);
2359b932e9eSAndre Oppermann static void	ip_forward(struct mbuf *m, int srcrt);
2364d77a549SAlfred Perlstein static void	ip_freef(struct ipqhead *, struct ipq *);
2378948e4baSArchie Cobbs 
238df8bae1dSRodney W. Grimes /*
239df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
240df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
241df8bae1dSRodney W. Grimes  */
242df8bae1dSRodney W. Grimes void
243df8bae1dSRodney W. Grimes ip_init()
244df8bae1dSRodney W. Grimes {
245f0ffb944SJulian Elischer 	register struct protosw *pr;
246df8bae1dSRodney W. Grimes 	register int i;
247df8bae1dSRodney W. Grimes 
24859562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
249ca925d9cSJonathan Lemon 	in_ifaddrhashtbl = hashinit(INADDR_NHASH, M_IFADDR, &in_ifaddrhmask);
250f0ffb944SJulian Elischer 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
251df8bae1dSRodney W. Grimes 	if (pr == 0)
252db09bef3SAndre Oppermann 		panic("ip_init: PF_INET not found");
253db09bef3SAndre Oppermann 
254db09bef3SAndre Oppermann 	/* Initialize the entire ip_protox[] array to IPPROTO_RAW. */
255df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
256df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
257db09bef3SAndre Oppermann 	/*
258db09bef3SAndre Oppermann 	 * Cycle through IP protocols and put them into the appropriate place
259db09bef3SAndre Oppermann 	 * in ip_protox[].
260db09bef3SAndre Oppermann 	 */
261f0ffb944SJulian Elischer 	for (pr = inetdomain.dom_protosw;
262f0ffb944SJulian Elischer 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
263df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
264db09bef3SAndre Oppermann 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) {
265db09bef3SAndre Oppermann 			/* Be careful to only index valid IP protocols. */
266de38924dSAndre Oppermann 			if (pr->pr_protocol <= IPPROTO_MAX)
267df8bae1dSRodney W. Grimes 				ip_protox[pr->pr_protocol] = pr - inetsw;
268db09bef3SAndre Oppermann 		}
269194a213eSAndrey A. Chernov 
270c21fd232SAndre Oppermann 	/* Initialize packet filter hooks. */
271134ea224SSam Leffler 	inet_pfil_hook.ph_type = PFIL_TYPE_AF;
272134ea224SSam Leffler 	inet_pfil_hook.ph_af = AF_INET;
273134ea224SSam Leffler 	if ((i = pfil_head_register(&inet_pfil_hook)) != 0)
274134ea224SSam Leffler 		printf("%s: WARNING: unable to register pfil hook, "
275134ea224SSam Leffler 			"error %d\n", __func__, i);
276134ea224SSam Leffler 
277db09bef3SAndre Oppermann 	/* Initialize IP reassembly queue. */
2782fad1e93SSam Leffler 	IPQ_LOCK_INIT();
279194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
280462b86feSPoul-Henning Kamp 	    TAILQ_INIT(&ipq[i]);
281375386e2SMike Silbersack 	maxnipq = nmbclusters / 32;
282375386e2SMike Silbersack 	maxfragsperpacket = 16;
283194a213eSAndrey A. Chernov 
2845f311da2SMike Silbersack 	/* Start ipport_tick. */
2855f311da2SMike Silbersack 	callout_init(&ipport_tick_callout, CALLOUT_MPSAFE);
2865f311da2SMike Silbersack 	ipport_tick(NULL);
2875f311da2SMike Silbersack 	EVENTHANDLER_REGISTER(shutdown_pre_sync, ip_fini, NULL,
2885f311da2SMike Silbersack 		SHUTDOWN_PRI_DEFAULT);
2895f311da2SMike Silbersack 
290db09bef3SAndre Oppermann 	/* Initialize various other remaining things. */
291227ee8a1SPoul-Henning Kamp 	ip_id = time_second & 0xffff;
292df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
2936008862bSJohn Baldwin 	mtx_init(&ipintrq.ifq_mtx, "ip_inq", NULL, MTX_DEF);
2947902224cSSam Leffler 	netisr_register(NETISR_IP, ip_input, &ipintrq, NETISR_MPSAFE);
295df8bae1dSRodney W. Grimes }
296df8bae1dSRodney W. Grimes 
2975f311da2SMike Silbersack void ip_fini(xtp)
2985f311da2SMike Silbersack 	void *xtp;
2995f311da2SMike Silbersack {
3005f311da2SMike Silbersack 	callout_stop(&ipport_tick_callout);
3015f311da2SMike Silbersack }
3025f311da2SMike Silbersack 
3034d2e3692SLuigi Rizzo /*
304df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
305df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
306df8bae1dSRodney W. Grimes  */
307c67b1d17SGarrett Wollman void
308c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
309df8bae1dSRodney W. Grimes {
3109188b4a1SAndre Oppermann 	struct ip *ip = NULL;
3115da9f8faSJosef Karthauser 	struct in_ifaddr *ia = NULL;
312ca925d9cSJonathan Lemon 	struct ifaddr *ifa;
3139b932e9eSAndre Oppermann 	int    checkif, hlen = 0;
31447c861ecSBrian Somers 	u_short sum;
31502c1c707SAndre Oppermann 	int dchg = 0;				/* dest changed after fw */
316f51f805fSSam Leffler 	struct in_addr odst;			/* original dst address */
317b9234fafSSam Leffler #ifdef FAST_IPSEC
31836e8826fSMax Laier 	struct m_tag *mtag;
319b9234fafSSam Leffler 	struct tdb_ident *tdbi;
320b9234fafSSam Leffler 	struct secpolicy *sp;
321b9234fafSSam Leffler 	int s, error;
322b9234fafSSam Leffler #endif /* FAST_IPSEC */
323b715f178SLuigi Rizzo 
324fe584538SDag-Erling Smørgrav   	M_ASSERTPKTHDR(m);
325db40007dSAndrew R. Reiter 
326ac9d7e26SMax Laier 	if (m->m_flags & M_FASTFWD_OURS) {
3279b932e9eSAndre Oppermann 		/*
32876ff6dcfSAndre Oppermann 		 * Firewall or NAT changed destination to local.
32976ff6dcfSAndre Oppermann 		 * We expect ip_len and ip_off to be in host byte order.
3309b932e9eSAndre Oppermann 		 */
33176ff6dcfSAndre Oppermann 		m->m_flags &= ~M_FASTFWD_OURS;
33276ff6dcfSAndre Oppermann 		/* Set up some basics that will be used later. */
3332b25acc1SLuigi Rizzo 		ip = mtod(m, struct ip *);
33453be11f6SPoul-Henning Kamp 		hlen = ip->ip_hl << 2;
3359b932e9eSAndre Oppermann   		goto ours;
3362b25acc1SLuigi Rizzo   	}
3372b25acc1SLuigi Rizzo 
338df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
33958938916SGarrett Wollman 
34058938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
34158938916SGarrett Wollman 		goto tooshort;
34258938916SGarrett Wollman 
343df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
3440b17fba7SAndre Oppermann 	    (m = m_pullup(m, sizeof (struct ip))) == NULL) {
345df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
346c67b1d17SGarrett Wollman 		return;
347df8bae1dSRodney W. Grimes 	}
348df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
34958938916SGarrett Wollman 
35053be11f6SPoul-Henning Kamp 	if (ip->ip_v != IPVERSION) {
351df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
352df8bae1dSRodney W. Grimes 		goto bad;
353df8bae1dSRodney W. Grimes 	}
35458938916SGarrett Wollman 
35553be11f6SPoul-Henning Kamp 	hlen = ip->ip_hl << 2;
356df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
357df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
358df8bae1dSRodney W. Grimes 		goto bad;
359df8bae1dSRodney W. Grimes 	}
360df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
3610b17fba7SAndre Oppermann 		if ((m = m_pullup(m, hlen)) == NULL) {
362df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
363c67b1d17SGarrett Wollman 			return;
364df8bae1dSRodney W. Grimes 		}
365df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
366df8bae1dSRodney W. Grimes 	}
36733841545SHajimu UMEMOTO 
36833841545SHajimu UMEMOTO 	/* 127/8 must not appear on wire - RFC1122 */
36933841545SHajimu UMEMOTO 	if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
37033841545SHajimu UMEMOTO 	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
37133841545SHajimu UMEMOTO 		if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
37233841545SHajimu UMEMOTO 			ipstat.ips_badaddr++;
37333841545SHajimu UMEMOTO 			goto bad;
37433841545SHajimu UMEMOTO 		}
37533841545SHajimu UMEMOTO 	}
37633841545SHajimu UMEMOTO 
377db4f9cc7SJonathan Lemon 	if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) {
378db4f9cc7SJonathan Lemon 		sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
379db4f9cc7SJonathan Lemon 	} else {
38058938916SGarrett Wollman 		if (hlen == sizeof(struct ip)) {
38147c861ecSBrian Somers 			sum = in_cksum_hdr(ip);
38258938916SGarrett Wollman 		} else {
38347c861ecSBrian Somers 			sum = in_cksum(m, hlen);
38458938916SGarrett Wollman 		}
385db4f9cc7SJonathan Lemon 	}
38647c861ecSBrian Somers 	if (sum) {
387df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
388df8bae1dSRodney W. Grimes 		goto bad;
389df8bae1dSRodney W. Grimes 	}
390df8bae1dSRodney W. Grimes 
39102b199f1SMax Laier #ifdef ALTQ
39202b199f1SMax Laier 	if (altq_input != NULL && (*altq_input)(m, AF_INET) == 0)
39302b199f1SMax Laier 		/* packet is dropped by traffic conditioner */
39402b199f1SMax Laier 		return;
39502b199f1SMax Laier #endif
39602b199f1SMax Laier 
397df8bae1dSRodney W. Grimes 	/*
398df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
399df8bae1dSRodney W. Grimes 	 */
400fd8e4ebcSMike Barcroft 	ip->ip_len = ntohs(ip->ip_len);
401df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
402df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
403df8bae1dSRodney W. Grimes 		goto bad;
404df8bae1dSRodney W. Grimes 	}
405fd8e4ebcSMike Barcroft 	ip->ip_off = ntohs(ip->ip_off);
406df8bae1dSRodney W. Grimes 
407df8bae1dSRodney W. Grimes 	/*
408df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
409df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
410df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
411df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
412df8bae1dSRodney W. Grimes 	 */
413df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
41458938916SGarrett Wollman tooshort:
415df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
416df8bae1dSRodney W. Grimes 		goto bad;
417df8bae1dSRodney W. Grimes 	}
418df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
419df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
420df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
421df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
422df8bae1dSRodney W. Grimes 		} else
423df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
424df8bae1dSRodney W. Grimes 	}
42514dd6717SSam Leffler #if defined(IPSEC) && !defined(IPSEC_FILTERGIF)
42614dd6717SSam Leffler 	/*
42714dd6717SSam Leffler 	 * Bypass packet filtering for packets from a tunnel (gif).
42814dd6717SSam Leffler 	 */
4290f9ade71SHajimu UMEMOTO 	if (ipsec_getnhist(m))
430c21fd232SAndre Oppermann 		goto passin;
43114dd6717SSam Leffler #endif
4321f76a5e2SSam Leffler #if defined(FAST_IPSEC) && !defined(IPSEC_FILTERGIF)
4331f76a5e2SSam Leffler 	/*
4341f76a5e2SSam Leffler 	 * Bypass packet filtering for packets from a tunnel (gif).
4351f76a5e2SSam Leffler 	 */
4361f76a5e2SSam Leffler 	if (m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
437c21fd232SAndre Oppermann 		goto passin;
4381f76a5e2SSam Leffler #endif
4393f67c834SDon Lewis 
440c4ac87eaSDarren Reed 	/*
441134ea224SSam Leffler 	 * Run through list of hooks for input packets.
442f51f805fSSam Leffler 	 *
443f51f805fSSam Leffler 	 * NB: Beware of the destination address changing (e.g.
444f51f805fSSam Leffler 	 *     by NAT rewriting).  When this happens, tell
445f51f805fSSam Leffler 	 *     ip_forward to do the right thing.
446c4ac87eaSDarren Reed 	 */
447c21fd232SAndre Oppermann 
448c21fd232SAndre Oppermann 	/* Jump over all PFIL processing if hooks are not active. */
449c21fd232SAndre Oppermann 	if (inet_pfil_hook.ph_busy_count == -1)
450c21fd232SAndre Oppermann 		goto passin;
451c21fd232SAndre Oppermann 
452f51f805fSSam Leffler 	odst = ip->ip_dst;
453134ea224SSam Leffler 	if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif,
454d6a8d588SMax Laier 	    PFIL_IN, NULL) != 0)
455beec8214SDarren Reed 		return;
456134ea224SSam Leffler 	if (m == NULL)			/* consumed by filter */
457c4ac87eaSDarren Reed 		return;
4589b932e9eSAndre Oppermann 
459c4ac87eaSDarren Reed 	ip = mtod(m, struct ip *);
46002c1c707SAndre Oppermann 	dchg = (odst.s_addr != ip->ip_dst.s_addr);
4619b932e9eSAndre Oppermann 
4629b932e9eSAndre Oppermann #ifdef IPFIREWALL_FORWARD
4639b932e9eSAndre Oppermann 	if (m->m_flags & M_FASTFWD_OURS) {
4649b932e9eSAndre Oppermann 		m->m_flags &= ~M_FASTFWD_OURS;
4659b932e9eSAndre Oppermann 		goto ours;
4669b932e9eSAndre Oppermann 	}
4679b932e9eSAndre Oppermann 	dchg = (m_tag_find(m, PACKET_TAG_IPFORWARD, NULL) != NULL);
4689b932e9eSAndre Oppermann #endif /* IPFIREWALL_FORWARD */
4699b932e9eSAndre Oppermann 
470c21fd232SAndre Oppermann passin:
471df8bae1dSRodney W. Grimes 	/*
472df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
473df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
474df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
475df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
476df8bae1dSRodney W. Grimes 	 */
4779b932e9eSAndre Oppermann 	if (hlen > sizeof (struct ip) && ip_dooptions(m, 0))
478c67b1d17SGarrett Wollman 		return;
479df8bae1dSRodney W. Grimes 
480f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
481f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
482f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
483f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
484f0068c4aSGarrett Wollman 	 * grabbing the packet.
485f0068c4aSGarrett Wollman          */
4861c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
487f0068c4aSGarrett Wollman 		goto ours;
488f0068c4aSGarrett Wollman 
489df8bae1dSRodney W. Grimes 	/*
490df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
491cc766e04SGarrett Wollman 	 * If we don't have any addresses, assume any unicast packet
492cc766e04SGarrett Wollman 	 * we receive might be for us (and let the upper layers deal
493cc766e04SGarrett Wollman 	 * with it).
494df8bae1dSRodney W. Grimes 	 */
495cc766e04SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead) &&
496cc766e04SGarrett Wollman 	    (m->m_flags & (M_MCAST|M_BCAST)) == 0)
497cc766e04SGarrett Wollman 		goto ours;
498cc766e04SGarrett Wollman 
4997538a9a0SJonathan Lemon 	/*
500823db0e9SDon Lewis 	 * Enable a consistency check between the destination address
501823db0e9SDon Lewis 	 * and the arrival interface for a unicast packet (the RFC 1122
502823db0e9SDon Lewis 	 * strong ES model) if IP forwarding is disabled and the packet
503e15ae1b2SDon Lewis 	 * is not locally generated and the packet is not subject to
504e15ae1b2SDon Lewis 	 * 'ipfw fwd'.
5053f67c834SDon Lewis 	 *
5063f67c834SDon Lewis 	 * XXX - Checking also should be disabled if the destination
5073f67c834SDon Lewis 	 * address is ipnat'ed to a different interface.
5083f67c834SDon Lewis 	 *
509a8f12100SDon Lewis 	 * XXX - Checking is incompatible with IP aliases added
5103f67c834SDon Lewis 	 * to the loopback interface instead of the interface where
5113f67c834SDon Lewis 	 * the packets are received.
512823db0e9SDon Lewis 	 */
513823db0e9SDon Lewis 	checkif = ip_checkinterface && (ipforwarding == 0) &&
5149494d596SBrooks Davis 	    m->m_pkthdr.rcvif != NULL &&
515e15ae1b2SDon Lewis 	    ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) &&
5169b932e9eSAndre Oppermann 	    (dchg == 0);
517823db0e9SDon Lewis 
518ca925d9cSJonathan Lemon 	/*
519ca925d9cSJonathan Lemon 	 * Check for exact addresses in the hash bucket.
520ca925d9cSJonathan Lemon 	 */
5219b932e9eSAndre Oppermann 	LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
522f9e354dfSJulian Elischer 		/*
523823db0e9SDon Lewis 		 * If the address matches, verify that the packet
524823db0e9SDon Lewis 		 * arrived via the correct interface if checking is
525823db0e9SDon Lewis 		 * enabled.
526f9e354dfSJulian Elischer 		 */
5279b932e9eSAndre Oppermann 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr &&
528823db0e9SDon Lewis 		    (!checkif || ia->ia_ifp == m->m_pkthdr.rcvif))
529ed1ff184SJulian Elischer 			goto ours;
530ca925d9cSJonathan Lemon 	}
531823db0e9SDon Lewis 	/*
532ca925d9cSJonathan Lemon 	 * Check for broadcast addresses.
533ca925d9cSJonathan Lemon 	 *
534ca925d9cSJonathan Lemon 	 * Only accept broadcast packets that arrive via the matching
535ca925d9cSJonathan Lemon 	 * interface.  Reception of forwarded directed broadcasts would
536ca925d9cSJonathan Lemon 	 * be handled via ip_forward() and ether_output() with the loopback
537ca925d9cSJonathan Lemon 	 * into the stack for SIMPLEX interfaces handled by ether_output().
538823db0e9SDon Lewis 	 */
5394f450ff9SBruce M Simpson 	if (m->m_pkthdr.rcvif != NULL &&
5404f450ff9SBruce M Simpson 	    m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) {
541ca925d9cSJonathan Lemon 	        TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) {
542ca925d9cSJonathan Lemon 			if (ifa->ifa_addr->sa_family != AF_INET)
543ca925d9cSJonathan Lemon 				continue;
544ca925d9cSJonathan Lemon 			ia = ifatoia(ifa);
545df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
5469b932e9eSAndre Oppermann 			    ip->ip_dst.s_addr)
547df8bae1dSRodney W. Grimes 				goto ours;
5489b932e9eSAndre Oppermann 			if (ia->ia_netbroadcast.s_addr == ip->ip_dst.s_addr)
549df8bae1dSRodney W. Grimes 				goto ours;
5500ac40133SBrian Somers #ifdef BOOTP_COMPAT
551ca925d9cSJonathan Lemon 			if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
552ca925d9cSJonathan Lemon 				goto ours;
5530ac40133SBrian Somers #endif
554df8bae1dSRodney W. Grimes 		}
555df8bae1dSRodney W. Grimes 	}
556df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
557df8bae1dSRodney W. Grimes 		struct in_multi *inm;
558df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
559df8bae1dSRodney W. Grimes 			/*
560df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
561df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
562df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
563df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
564df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
565df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
566df8bae1dSRodney W. Grimes 			 */
567bbb4330bSLuigi Rizzo 			if (ip_mforward &&
568bbb4330bSLuigi Rizzo 			    ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
569df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
570df8bae1dSRodney W. Grimes 				m_freem(m);
571c67b1d17SGarrett Wollman 				return;
572df8bae1dSRodney W. Grimes 			}
573df8bae1dSRodney W. Grimes 
574df8bae1dSRodney W. Grimes 			/*
57511612afaSDima Dorfman 			 * The process-level routing daemon needs to receive
576df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
577df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
578df8bae1dSRodney W. Grimes 			 */
579df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
580df8bae1dSRodney W. Grimes 				goto ours;
581df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
582df8bae1dSRodney W. Grimes 		}
583df8bae1dSRodney W. Grimes 		/*
584df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
585df8bae1dSRodney W. Grimes 		 * arrival interface.
586df8bae1dSRodney W. Grimes 		 */
587df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
588df8bae1dSRodney W. Grimes 		if (inm == NULL) {
58982c39223SGarrett Wollman 			ipstat.ips_notmember++;
590df8bae1dSRodney W. Grimes 			m_freem(m);
591c67b1d17SGarrett Wollman 			return;
592df8bae1dSRodney W. Grimes 		}
593df8bae1dSRodney W. Grimes 		goto ours;
594df8bae1dSRodney W. Grimes 	}
595df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
596df8bae1dSRodney W. Grimes 		goto ours;
597df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
598df8bae1dSRodney W. Grimes 		goto ours;
599df8bae1dSRodney W. Grimes 
6006a800098SYoshinobu Inoue 	/*
6016a800098SYoshinobu Inoue 	 * FAITH(Firewall Aided Internet Translator)
6026a800098SYoshinobu Inoue 	 */
6036a800098SYoshinobu Inoue 	if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
6046a800098SYoshinobu Inoue 		if (ip_keepfaith) {
6056a800098SYoshinobu Inoue 			if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_ICMP)
6066a800098SYoshinobu Inoue 				goto ours;
6076a800098SYoshinobu Inoue 		}
6086a800098SYoshinobu Inoue 		m_freem(m);
6096a800098SYoshinobu Inoue 		return;
6106a800098SYoshinobu Inoue 	}
6119494d596SBrooks Davis 
612df8bae1dSRodney W. Grimes 	/*
613df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
614df8bae1dSRodney W. Grimes 	 */
615df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
616df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
617df8bae1dSRodney W. Grimes 		m_freem(m);
618546f251bSChris D. Faulhaber 	} else {
619546f251bSChris D. Faulhaber #ifdef IPSEC
620546f251bSChris D. Faulhaber 		/*
621546f251bSChris D. Faulhaber 		 * Enforce inbound IPsec SPD.
622546f251bSChris D. Faulhaber 		 */
623546f251bSChris D. Faulhaber 		if (ipsec4_in_reject(m, NULL)) {
624546f251bSChris D. Faulhaber 			ipsecstat.in_polvio++;
625546f251bSChris D. Faulhaber 			goto bad;
626546f251bSChris D. Faulhaber 		}
627546f251bSChris D. Faulhaber #endif /* IPSEC */
628b9234fafSSam Leffler #ifdef FAST_IPSEC
629b9234fafSSam Leffler 		mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
630b9234fafSSam Leffler 		s = splnet();
631b9234fafSSam Leffler 		if (mtag != NULL) {
632b9234fafSSam Leffler 			tdbi = (struct tdb_ident *)(mtag + 1);
633b9234fafSSam Leffler 			sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND);
634b9234fafSSam Leffler 		} else {
635b9234fafSSam Leffler 			sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
636b9234fafSSam Leffler 						   IP_FORWARDING, &error);
637b9234fafSSam Leffler 		}
638b9234fafSSam Leffler 		if (sp == NULL) {	/* NB: can happen if error */
639b9234fafSSam Leffler 			splx(s);
640b9234fafSSam Leffler 			/*XXX error stat???*/
641b9234fafSSam Leffler 			DPRINTF(("ip_input: no SP for forwarding\n"));	/*XXX*/
642b9234fafSSam Leffler 			goto bad;
643b9234fafSSam Leffler 		}
644b9234fafSSam Leffler 
645b9234fafSSam Leffler 		/*
646b9234fafSSam Leffler 		 * Check security policy against packet attributes.
647b9234fafSSam Leffler 		 */
648b9234fafSSam Leffler 		error = ipsec_in_reject(sp, m);
649b9234fafSSam Leffler 		KEY_FREESP(&sp);
650b9234fafSSam Leffler 		splx(s);
651b9234fafSSam Leffler 		if (error) {
652b9234fafSSam Leffler 			ipstat.ips_cantforward++;
653b9234fafSSam Leffler 			goto bad;
654b9234fafSSam Leffler 		}
655b9234fafSSam Leffler #endif /* FAST_IPSEC */
6569b932e9eSAndre Oppermann 		ip_forward(m, dchg);
657546f251bSChris D. Faulhaber 	}
658c67b1d17SGarrett Wollman 	return;
659df8bae1dSRodney W. Grimes 
660df8bae1dSRodney W. Grimes ours:
661d0ebc0d2SYaroslav Tykhiy #ifdef IPSTEALTH
662d0ebc0d2SYaroslav Tykhiy 	/*
663d0ebc0d2SYaroslav Tykhiy 	 * IPSTEALTH: Process non-routing options only
664d0ebc0d2SYaroslav Tykhiy 	 * if the packet is destined for us.
665d0ebc0d2SYaroslav Tykhiy 	 */
6662b25acc1SLuigi Rizzo 	if (ipstealth && hlen > sizeof (struct ip) &&
6679b932e9eSAndre Oppermann 	    ip_dooptions(m, 1))
668d0ebc0d2SYaroslav Tykhiy 		return;
669d0ebc0d2SYaroslav Tykhiy #endif /* IPSTEALTH */
670d0ebc0d2SYaroslav Tykhiy 
6715da9f8faSJosef Karthauser 	/* Count the packet in the ip address stats */
6725da9f8faSJosef Karthauser 	if (ia != NULL) {
6735da9f8faSJosef Karthauser 		ia->ia_ifa.if_ipackets++;
6745da9f8faSJosef Karthauser 		ia->ia_ifa.if_ibytes += m->m_pkthdr.len;
6755da9f8faSJosef Karthauser 	}
676100ba1a6SJordan K. Hubbard 
67763f8d699SJordan K. Hubbard 	/*
678b6ea1aa5SRuslan Ermilov 	 * Attempt reassembly; if it succeeds, proceed.
679ac9d7e26SMax Laier 	 * ip_reass() will return a different mbuf.
680df8bae1dSRodney W. Grimes 	 */
681f0cada84SAndre Oppermann 	if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
682f0cada84SAndre Oppermann 		m = ip_reass(m);
683f0cada84SAndre Oppermann 		if (m == NULL)
684c67b1d17SGarrett Wollman 			return;
6856a800098SYoshinobu Inoue 		ip = mtod(m, struct ip *);
6867e2df452SRuslan Ermilov 		/* Get the header length of the reassembled packet */
68753be11f6SPoul-Henning Kamp 		hlen = ip->ip_hl << 2;
688f0cada84SAndre Oppermann 	}
689f0cada84SAndre Oppermann 
690f0cada84SAndre Oppermann 	/*
691f0cada84SAndre Oppermann 	 * Further protocols expect the packet length to be w/o the
692f0cada84SAndre Oppermann 	 * IP header.
693f0cada84SAndre Oppermann 	 */
694df8bae1dSRodney W. Grimes 	ip->ip_len -= hlen;
695df8bae1dSRodney W. Grimes 
69633841545SHajimu UMEMOTO #ifdef IPSEC
69733841545SHajimu UMEMOTO 	/*
69833841545SHajimu UMEMOTO 	 * enforce IPsec policy checking if we are seeing last header.
69933841545SHajimu UMEMOTO 	 * note that we do not visit this with protocols with pcb layer
70033841545SHajimu UMEMOTO 	 * code - like udp/tcp/raw ip.
70133841545SHajimu UMEMOTO 	 */
70233841545SHajimu UMEMOTO 	if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0 &&
70333841545SHajimu UMEMOTO 	    ipsec4_in_reject(m, NULL)) {
70433841545SHajimu UMEMOTO 		ipsecstat.in_polvio++;
70533841545SHajimu UMEMOTO 		goto bad;
70633841545SHajimu UMEMOTO 	}
70733841545SHajimu UMEMOTO #endif
708b9234fafSSam Leffler #if FAST_IPSEC
709b9234fafSSam Leffler 	/*
710b9234fafSSam Leffler 	 * enforce IPsec policy checking if we are seeing last header.
711b9234fafSSam Leffler 	 * note that we do not visit this with protocols with pcb layer
712b9234fafSSam Leffler 	 * code - like udp/tcp/raw ip.
713b9234fafSSam Leffler 	 */
714b9234fafSSam Leffler 	if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0) {
715b9234fafSSam Leffler 		/*
716b9234fafSSam Leffler 		 * Check if the packet has already had IPsec processing
717b9234fafSSam Leffler 		 * done.  If so, then just pass it along.  This tag gets
718b9234fafSSam Leffler 		 * set during AH, ESP, etc. input handling, before the
719b9234fafSSam Leffler 		 * packet is returned to the ip input queue for delivery.
720b9234fafSSam Leffler 		 */
721b9234fafSSam Leffler 		mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
722b9234fafSSam Leffler 		s = splnet();
723b9234fafSSam Leffler 		if (mtag != NULL) {
724b9234fafSSam Leffler 			tdbi = (struct tdb_ident *)(mtag + 1);
725b9234fafSSam Leffler 			sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND);
726b9234fafSSam Leffler 		} else {
727b9234fafSSam Leffler 			sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
728b9234fafSSam Leffler 						   IP_FORWARDING, &error);
729b9234fafSSam Leffler 		}
730b9234fafSSam Leffler 		if (sp != NULL) {
731b9234fafSSam Leffler 			/*
732b9234fafSSam Leffler 			 * Check security policy against packet attributes.
733b9234fafSSam Leffler 			 */
734b9234fafSSam Leffler 			error = ipsec_in_reject(sp, m);
735b9234fafSSam Leffler 			KEY_FREESP(&sp);
736b9234fafSSam Leffler 		} else {
737b9234fafSSam Leffler 			/* XXX error stat??? */
738b9234fafSSam Leffler 			error = EINVAL;
739b9234fafSSam Leffler DPRINTF(("ip_input: no SP, packet discarded\n"));/*XXX*/
740b9234fafSSam Leffler 			goto bad;
741b9234fafSSam Leffler 		}
742b9234fafSSam Leffler 		splx(s);
743b9234fafSSam Leffler 		if (error)
744b9234fafSSam Leffler 			goto bad;
745b9234fafSSam Leffler 	}
746b9234fafSSam Leffler #endif /* FAST_IPSEC */
74733841545SHajimu UMEMOTO 
748df8bae1dSRodney W. Grimes 	/*
749df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
750df8bae1dSRodney W. Grimes 	 */
751df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
7529b932e9eSAndre Oppermann 
7532b25acc1SLuigi Rizzo 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
754c67b1d17SGarrett Wollman 	return;
755df8bae1dSRodney W. Grimes bad:
756df8bae1dSRodney W. Grimes 	m_freem(m);
757c67b1d17SGarrett Wollman }
758c67b1d17SGarrett Wollman 
759c67b1d17SGarrett Wollman /*
7608948e4baSArchie Cobbs  * Take incoming datagram fragment and try to reassemble it into
761f0cada84SAndre Oppermann  * whole datagram.  If the argument is the first fragment or one
762f0cada84SAndre Oppermann  * in between the function will return NULL and store the mbuf
763f0cada84SAndre Oppermann  * in the fragment chain.  If the argument is the last fragment
764f0cada84SAndre Oppermann  * the packet will be reassembled and the pointer to the new
765f0cada84SAndre Oppermann  * mbuf returned for further processing.  Only m_tags attached
766f0cada84SAndre Oppermann  * to the first packet/fragment are preserved.
767f0cada84SAndre Oppermann  * The IP header is *NOT* adjusted out of iplen.
768df8bae1dSRodney W. Grimes  */
7698948e4baSArchie Cobbs 
770f0cada84SAndre Oppermann struct mbuf *
771f0cada84SAndre Oppermann ip_reass(struct mbuf *m)
772df8bae1dSRodney W. Grimes {
773f0cada84SAndre Oppermann 	struct ip *ip;
774f0cada84SAndre Oppermann 	struct mbuf *p, *q, *nq, *t;
775f0cada84SAndre Oppermann 	struct ipq *fp = NULL;
776f0cada84SAndre Oppermann 	struct ipqhead *head;
777f0cada84SAndre Oppermann 	int i, hlen, next;
77859dfcba4SHajimu UMEMOTO 	u_int8_t ecn, ecn0;
779f0cada84SAndre Oppermann 	u_short hash;
780df8bae1dSRodney W. Grimes 
781f0cada84SAndre Oppermann 	/* If maxnipq is 0, never accept fragments. */
782f0cada84SAndre Oppermann 	if (maxnipq == 0) {
783f0cada84SAndre Oppermann 		ipstat.ips_fragments++;
784f0cada84SAndre Oppermann 		ipstat.ips_fragdropped++;
7859d804f81SAndre Oppermann 		m_freem(m);
7869d804f81SAndre Oppermann 		return (NULL);
787f0cada84SAndre Oppermann 	}
7882fad1e93SSam Leffler 
789f0cada84SAndre Oppermann 	ip = mtod(m, struct ip *);
790f0cada84SAndre Oppermann 	hlen = ip->ip_hl << 2;
791f0cada84SAndre Oppermann 
792f0cada84SAndre Oppermann 	hash = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
793f0cada84SAndre Oppermann 	head = &ipq[hash];
794f0cada84SAndre Oppermann 	IPQ_LOCK();
795f0cada84SAndre Oppermann 
796f0cada84SAndre Oppermann 	/*
797f0cada84SAndre Oppermann 	 * Look for queue of fragments
798f0cada84SAndre Oppermann 	 * of this datagram.
799f0cada84SAndre Oppermann 	 */
800f0cada84SAndre Oppermann 	TAILQ_FOREACH(fp, head, ipq_list)
801f0cada84SAndre Oppermann 		if (ip->ip_id == fp->ipq_id &&
802f0cada84SAndre Oppermann 		    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
803f0cada84SAndre Oppermann 		    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
804f0cada84SAndre Oppermann #ifdef MAC
805f0cada84SAndre Oppermann 		    mac_fragment_match(m, fp) &&
806f0cada84SAndre Oppermann #endif
807f0cada84SAndre Oppermann 		    ip->ip_p == fp->ipq_p)
808f0cada84SAndre Oppermann 			goto found;
809f0cada84SAndre Oppermann 
810f0cada84SAndre Oppermann 	fp = NULL;
811f0cada84SAndre Oppermann 
812f0cada84SAndre Oppermann 	/*
813f0cada84SAndre Oppermann 	 * Enforce upper bound on number of fragmented packets
814f0cada84SAndre Oppermann 	 * for which we attempt reassembly;
815f0cada84SAndre Oppermann 	 * If maxnipq is -1, accept all fragments without limitation.
816f0cada84SAndre Oppermann 	 */
817f0cada84SAndre Oppermann 	if ((nipq > maxnipq) && (maxnipq > 0)) {
818f0cada84SAndre Oppermann 		/*
819f0cada84SAndre Oppermann 		 * drop something from the tail of the current queue
820f0cada84SAndre Oppermann 		 * before proceeding further
821f0cada84SAndre Oppermann 		 */
822f0cada84SAndre Oppermann 		struct ipq *q = TAILQ_LAST(head, ipqhead);
823f0cada84SAndre Oppermann 		if (q == NULL) {   /* gak */
824f0cada84SAndre Oppermann 			for (i = 0; i < IPREASS_NHASH; i++) {
825f0cada84SAndre Oppermann 				struct ipq *r = TAILQ_LAST(&ipq[i], ipqhead);
826f0cada84SAndre Oppermann 				if (r) {
827f0cada84SAndre Oppermann 					ipstat.ips_fragtimeout += r->ipq_nfrags;
828f0cada84SAndre Oppermann 					ip_freef(&ipq[i], r);
829f0cada84SAndre Oppermann 					break;
830f0cada84SAndre Oppermann 				}
831f0cada84SAndre Oppermann 			}
832f0cada84SAndre Oppermann 		} else {
833f0cada84SAndre Oppermann 			ipstat.ips_fragtimeout += q->ipq_nfrags;
834f0cada84SAndre Oppermann 			ip_freef(head, q);
835f0cada84SAndre Oppermann 		}
836f0cada84SAndre Oppermann 	}
837f0cada84SAndre Oppermann 
838f0cada84SAndre Oppermann found:
839f0cada84SAndre Oppermann 	/*
840f0cada84SAndre Oppermann 	 * Adjust ip_len to not reflect header,
841f0cada84SAndre Oppermann 	 * convert offset of this to bytes.
842f0cada84SAndre Oppermann 	 */
843f0cada84SAndre Oppermann 	ip->ip_len -= hlen;
844f0cada84SAndre Oppermann 	if (ip->ip_off & IP_MF) {
845f0cada84SAndre Oppermann 		/*
846f0cada84SAndre Oppermann 		 * Make sure that fragments have a data length
847f0cada84SAndre Oppermann 		 * that's a non-zero multiple of 8 bytes.
848f0cada84SAndre Oppermann 		 */
849f0cada84SAndre Oppermann 		if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
850f0cada84SAndre Oppermann 			ipstat.ips_toosmall++; /* XXX */
851f0cada84SAndre Oppermann 			goto dropfrag;
852f0cada84SAndre Oppermann 		}
853f0cada84SAndre Oppermann 		m->m_flags |= M_FRAG;
854f0cada84SAndre Oppermann 	} else
855f0cada84SAndre Oppermann 		m->m_flags &= ~M_FRAG;
856f0cada84SAndre Oppermann 	ip->ip_off <<= 3;
857f0cada84SAndre Oppermann 
858f0cada84SAndre Oppermann 
859f0cada84SAndre Oppermann 	/*
860f0cada84SAndre Oppermann 	 * Attempt reassembly; if it succeeds, proceed.
861f0cada84SAndre Oppermann 	 * ip_reass() will return a different mbuf.
862f0cada84SAndre Oppermann 	 */
863f0cada84SAndre Oppermann 	ipstat.ips_fragments++;
864f0cada84SAndre Oppermann 	m->m_pkthdr.header = ip;
865f0cada84SAndre Oppermann 
866f0cada84SAndre Oppermann 	/* Previous ip_reass() started here. */
867df8bae1dSRodney W. Grimes 	/*
868df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
869df8bae1dSRodney W. Grimes 	 * would confuse code below.
870df8bae1dSRodney W. Grimes 	 */
871df8bae1dSRodney W. Grimes 	m->m_data += hlen;
872df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
873df8bae1dSRodney W. Grimes 
874df8bae1dSRodney W. Grimes 	/*
875df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
876df8bae1dSRodney W. Grimes 	 */
877042bbfa3SRobert Watson 	if (fp == NULL) {
878a163d034SWarner Losh 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
879df8bae1dSRodney W. Grimes 			goto dropfrag;
880df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
88136b0360bSRobert Watson #ifdef MAC
8825e7ce478SRobert Watson 		if (mac_init_ipq(fp, M_NOWAIT) != 0) {
8835e7ce478SRobert Watson 			m_free(t);
8845e7ce478SRobert Watson 			goto dropfrag;
8855e7ce478SRobert Watson 		}
88636b0360bSRobert Watson 		mac_create_ipq(m, fp);
88736b0360bSRobert Watson #endif
888462b86feSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(head, fp, ipq_list);
889194a213eSAndrey A. Chernov 		nipq++;
890375386e2SMike Silbersack 		fp->ipq_nfrags = 1;
891df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
892df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
893df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
8946effc713SDoug Rabson 		fp->ipq_src = ip->ip_src;
8956effc713SDoug Rabson 		fp->ipq_dst = ip->ip_dst;
896af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
897af38c68cSLuigi Rizzo 		m->m_nextpkt = NULL;
898af38c68cSLuigi Rizzo 		goto inserted;
89936b0360bSRobert Watson 	} else {
900375386e2SMike Silbersack 		fp->ipq_nfrags++;
90136b0360bSRobert Watson #ifdef MAC
90236b0360bSRobert Watson 		mac_update_ipq(m, fp);
90336b0360bSRobert Watson #endif
904df8bae1dSRodney W. Grimes 	}
905df8bae1dSRodney W. Grimes 
9066effc713SDoug Rabson #define GETIP(m)	((struct ip*)((m)->m_pkthdr.header))
9076effc713SDoug Rabson 
908df8bae1dSRodney W. Grimes 	/*
90959dfcba4SHajimu UMEMOTO 	 * Handle ECN by comparing this segment with the first one;
91059dfcba4SHajimu UMEMOTO 	 * if CE is set, do not lose CE.
91159dfcba4SHajimu UMEMOTO 	 * drop if CE and not-ECT are mixed for the same packet.
91259dfcba4SHajimu UMEMOTO 	 */
91359dfcba4SHajimu UMEMOTO 	ecn = ip->ip_tos & IPTOS_ECN_MASK;
91459dfcba4SHajimu UMEMOTO 	ecn0 = GETIP(fp->ipq_frags)->ip_tos & IPTOS_ECN_MASK;
91559dfcba4SHajimu UMEMOTO 	if (ecn == IPTOS_ECN_CE) {
91659dfcba4SHajimu UMEMOTO 		if (ecn0 == IPTOS_ECN_NOTECT)
91759dfcba4SHajimu UMEMOTO 			goto dropfrag;
91859dfcba4SHajimu UMEMOTO 		if (ecn0 != IPTOS_ECN_CE)
91959dfcba4SHajimu UMEMOTO 			GETIP(fp->ipq_frags)->ip_tos |= IPTOS_ECN_CE;
92059dfcba4SHajimu UMEMOTO 	}
92159dfcba4SHajimu UMEMOTO 	if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT)
92259dfcba4SHajimu UMEMOTO 		goto dropfrag;
92359dfcba4SHajimu UMEMOTO 
92459dfcba4SHajimu UMEMOTO 	/*
925df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
926df8bae1dSRodney W. Grimes 	 */
9276effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
9286effc713SDoug Rabson 		if (GETIP(q)->ip_off > ip->ip_off)
929df8bae1dSRodney W. Grimes 			break;
930df8bae1dSRodney W. Grimes 
931df8bae1dSRodney W. Grimes 	/*
932df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
933df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
934af38c68cSLuigi Rizzo 	 * segment.  If it provides all of our data, drop us, otherwise
935af38c68cSLuigi Rizzo 	 * stick new segment in the proper place.
936db4f9cc7SJonathan Lemon 	 *
937db4f9cc7SJonathan Lemon 	 * If some of the data is dropped from the the preceding
938db4f9cc7SJonathan Lemon 	 * segment, then it's checksum is invalidated.
939df8bae1dSRodney W. Grimes 	 */
9406effc713SDoug Rabson 	if (p) {
9416effc713SDoug Rabson 		i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;
942df8bae1dSRodney W. Grimes 		if (i > 0) {
943df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
944df8bae1dSRodney W. Grimes 				goto dropfrag;
9456a800098SYoshinobu Inoue 			m_adj(m, i);
946db4f9cc7SJonathan Lemon 			m->m_pkthdr.csum_flags = 0;
947df8bae1dSRodney W. Grimes 			ip->ip_off += i;
948df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
949df8bae1dSRodney W. Grimes 		}
950af38c68cSLuigi Rizzo 		m->m_nextpkt = p->m_nextpkt;
951af38c68cSLuigi Rizzo 		p->m_nextpkt = m;
952af38c68cSLuigi Rizzo 	} else {
953af38c68cSLuigi Rizzo 		m->m_nextpkt = fp->ipq_frags;
954af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
955df8bae1dSRodney W. Grimes 	}
956df8bae1dSRodney W. Grimes 
957df8bae1dSRodney W. Grimes 	/*
958df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
959df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
960df8bae1dSRodney W. Grimes 	 */
9616effc713SDoug Rabson 	for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;
962af38c68cSLuigi Rizzo 	     q = nq) {
963b36f5b37SMaxim Konovalov 		i = (ip->ip_off + ip->ip_len) - GETIP(q)->ip_off;
9646effc713SDoug Rabson 		if (i < GETIP(q)->ip_len) {
9656effc713SDoug Rabson 			GETIP(q)->ip_len -= i;
9666effc713SDoug Rabson 			GETIP(q)->ip_off += i;
9676effc713SDoug Rabson 			m_adj(q, i);
968db4f9cc7SJonathan Lemon 			q->m_pkthdr.csum_flags = 0;
969df8bae1dSRodney W. Grimes 			break;
970df8bae1dSRodney W. Grimes 		}
9716effc713SDoug Rabson 		nq = q->m_nextpkt;
972af38c68cSLuigi Rizzo 		m->m_nextpkt = nq;
97399e8617dSMaxim Konovalov 		ipstat.ips_fragdropped++;
974375386e2SMike Silbersack 		fp->ipq_nfrags--;
9756effc713SDoug Rabson 		m_freem(q);
976df8bae1dSRodney W. Grimes 	}
977df8bae1dSRodney W. Grimes 
978af38c68cSLuigi Rizzo inserted:
97993e0e116SJulian Elischer 
980df8bae1dSRodney W. Grimes 	/*
981375386e2SMike Silbersack 	 * Check for complete reassembly and perform frag per packet
982375386e2SMike Silbersack 	 * limiting.
983375386e2SMike Silbersack 	 *
984375386e2SMike Silbersack 	 * Frag limiting is performed here so that the nth frag has
985375386e2SMike Silbersack 	 * a chance to complete the packet before we drop the packet.
986375386e2SMike Silbersack 	 * As a result, n+1 frags are actually allowed per packet, but
987375386e2SMike Silbersack 	 * only n will ever be stored. (n = maxfragsperpacket.)
988375386e2SMike Silbersack 	 *
989df8bae1dSRodney W. Grimes 	 */
9906effc713SDoug Rabson 	next = 0;
9916effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
992375386e2SMike Silbersack 		if (GETIP(q)->ip_off != next) {
99399e8617dSMaxim Konovalov 			if (fp->ipq_nfrags > maxfragsperpacket) {
99499e8617dSMaxim Konovalov 				ipstat.ips_fragdropped += fp->ipq_nfrags;
995375386e2SMike Silbersack 				ip_freef(head, fp);
99699e8617dSMaxim Konovalov 			}
997f0cada84SAndre Oppermann 			goto done;
998375386e2SMike Silbersack 		}
9996effc713SDoug Rabson 		next += GETIP(q)->ip_len;
10006effc713SDoug Rabson 	}
10016effc713SDoug Rabson 	/* Make sure the last packet didn't have the IP_MF flag */
1002375386e2SMike Silbersack 	if (p->m_flags & M_FRAG) {
100399e8617dSMaxim Konovalov 		if (fp->ipq_nfrags > maxfragsperpacket) {
100499e8617dSMaxim Konovalov 			ipstat.ips_fragdropped += fp->ipq_nfrags;
1005375386e2SMike Silbersack 			ip_freef(head, fp);
100699e8617dSMaxim Konovalov 		}
1007f0cada84SAndre Oppermann 		goto done;
1008375386e2SMike Silbersack 	}
1009df8bae1dSRodney W. Grimes 
1010df8bae1dSRodney W. Grimes 	/*
1011430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
1012430d30d8SBill Fenner 	 */
10136effc713SDoug Rabson 	q = fp->ipq_frags;
10146effc713SDoug Rabson 	ip = GETIP(q);
101553be11f6SPoul-Henning Kamp 	if (next + (ip->ip_hl << 2) > IP_MAXPACKET) {
1016430d30d8SBill Fenner 		ipstat.ips_toolong++;
101799e8617dSMaxim Konovalov 		ipstat.ips_fragdropped += fp->ipq_nfrags;
1018462b86feSPoul-Henning Kamp 		ip_freef(head, fp);
1019f0cada84SAndre Oppermann 		goto done;
1020430d30d8SBill Fenner 	}
1021430d30d8SBill Fenner 
1022430d30d8SBill Fenner 	/*
1023430d30d8SBill Fenner 	 * Concatenate fragments.
1024df8bae1dSRodney W. Grimes 	 */
10256effc713SDoug Rabson 	m = q;
1026df8bae1dSRodney W. Grimes 	t = m->m_next;
1027df8bae1dSRodney W. Grimes 	m->m_next = 0;
1028df8bae1dSRodney W. Grimes 	m_cat(m, t);
10296effc713SDoug Rabson 	nq = q->m_nextpkt;
1030945aa40dSDoug Rabson 	q->m_nextpkt = 0;
10316effc713SDoug Rabson 	for (q = nq; q != NULL; q = nq) {
10326effc713SDoug Rabson 		nq = q->m_nextpkt;
1033945aa40dSDoug Rabson 		q->m_nextpkt = NULL;
1034db4f9cc7SJonathan Lemon 		m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;
1035db4f9cc7SJonathan Lemon 		m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;
1036a8db1d93SJonathan Lemon 		m_cat(m, q);
1037df8bae1dSRodney W. Grimes 	}
103836b0360bSRobert Watson #ifdef MAC
103936b0360bSRobert Watson 	mac_create_datagram_from_ipq(fp, m);
104036b0360bSRobert Watson 	mac_destroy_ipq(fp);
104136b0360bSRobert Watson #endif
1042df8bae1dSRodney W. Grimes 
1043df8bae1dSRodney W. Grimes 	/*
1044f0cada84SAndre Oppermann 	 * Create header for new ip packet by modifying header of first
1045f0cada84SAndre Oppermann 	 * packet;  dequeue and discard fragment reassembly header.
1046df8bae1dSRodney W. Grimes 	 * Make header visible.
1047df8bae1dSRodney W. Grimes 	 */
1048f0cada84SAndre Oppermann 	ip->ip_len = (ip->ip_hl << 2) + next;
10496effc713SDoug Rabson 	ip->ip_src = fp->ipq_src;
10506effc713SDoug Rabson 	ip->ip_dst = fp->ipq_dst;
1051462b86feSPoul-Henning Kamp 	TAILQ_REMOVE(head, fp, ipq_list);
1052194a213eSAndrey A. Chernov 	nipq--;
1053df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
105453be11f6SPoul-Henning Kamp 	m->m_len += (ip->ip_hl << 2);
105553be11f6SPoul-Henning Kamp 	m->m_data -= (ip->ip_hl << 2);
1056df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
1057a5554bf0SPoul-Henning Kamp 	if (m->m_flags & M_PKTHDR)	/* XXX this should be done elsewhere */
1058a5554bf0SPoul-Henning Kamp 		m_fixhdr(m);
1059f0cada84SAndre Oppermann 	ipstat.ips_reassembled++;
1060f0cada84SAndre Oppermann 	IPQ_UNLOCK();
10616a800098SYoshinobu Inoue 	return (m);
1062df8bae1dSRodney W. Grimes 
1063df8bae1dSRodney W. Grimes dropfrag:
1064df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
1065042bbfa3SRobert Watson 	if (fp != NULL)
1066375386e2SMike Silbersack 		fp->ipq_nfrags--;
1067df8bae1dSRodney W. Grimes 	m_freem(m);
1068f0cada84SAndre Oppermann done:
1069f0cada84SAndre Oppermann 	IPQ_UNLOCK();
1070f0cada84SAndre Oppermann 	return (NULL);
10716effc713SDoug Rabson 
10726effc713SDoug Rabson #undef GETIP
1073df8bae1dSRodney W. Grimes }
1074df8bae1dSRodney W. Grimes 
1075df8bae1dSRodney W. Grimes /*
1076df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
1077df8bae1dSRodney W. Grimes  * associated datagrams.
1078df8bae1dSRodney W. Grimes  */
10790312fbe9SPoul-Henning Kamp static void
1080462b86feSPoul-Henning Kamp ip_freef(fhp, fp)
1081462b86feSPoul-Henning Kamp 	struct ipqhead *fhp;
1082df8bae1dSRodney W. Grimes 	struct ipq *fp;
1083df8bae1dSRodney W. Grimes {
10846effc713SDoug Rabson 	register struct mbuf *q;
1085df8bae1dSRodney W. Grimes 
10862fad1e93SSam Leffler 	IPQ_LOCK_ASSERT();
10872fad1e93SSam Leffler 
10886effc713SDoug Rabson 	while (fp->ipq_frags) {
10896effc713SDoug Rabson 		q = fp->ipq_frags;
10906effc713SDoug Rabson 		fp->ipq_frags = q->m_nextpkt;
10916effc713SDoug Rabson 		m_freem(q);
1092df8bae1dSRodney W. Grimes 	}
1093462b86feSPoul-Henning Kamp 	TAILQ_REMOVE(fhp, fp, ipq_list);
1094df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
1095194a213eSAndrey A. Chernov 	nipq--;
1096df8bae1dSRodney W. Grimes }
1097df8bae1dSRodney W. Grimes 
1098df8bae1dSRodney W. Grimes /*
1099df8bae1dSRodney W. Grimes  * IP timer processing;
1100df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
1101df8bae1dSRodney W. Grimes  * queue, discard it.
1102df8bae1dSRodney W. Grimes  */
1103df8bae1dSRodney W. Grimes void
1104df8bae1dSRodney W. Grimes ip_slowtimo()
1105df8bae1dSRodney W. Grimes {
1106df8bae1dSRodney W. Grimes 	register struct ipq *fp;
1107df8bae1dSRodney W. Grimes 	int s = splnet();
1108194a213eSAndrey A. Chernov 	int i;
1109df8bae1dSRodney W. Grimes 
11102fad1e93SSam Leffler 	IPQ_LOCK();
1111194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
1112462b86feSPoul-Henning Kamp 		for(fp = TAILQ_FIRST(&ipq[i]); fp;) {
1113462b86feSPoul-Henning Kamp 			struct ipq *fpp;
1114462b86feSPoul-Henning Kamp 
1115462b86feSPoul-Henning Kamp 			fpp = fp;
1116462b86feSPoul-Henning Kamp 			fp = TAILQ_NEXT(fp, ipq_list);
1117462b86feSPoul-Henning Kamp 			if(--fpp->ipq_ttl == 0) {
111899e8617dSMaxim Konovalov 				ipstat.ips_fragtimeout += fpp->ipq_nfrags;
1119462b86feSPoul-Henning Kamp 				ip_freef(&ipq[i], fpp);
1120df8bae1dSRodney W. Grimes 			}
1121df8bae1dSRodney W. Grimes 		}
1122194a213eSAndrey A. Chernov 	}
1123690a6055SJesper Skriver 	/*
1124690a6055SJesper Skriver 	 * If we are over the maximum number of fragments
1125690a6055SJesper Skriver 	 * (due to the limit being lowered), drain off
1126690a6055SJesper Skriver 	 * enough to get down to the new limit.
1127690a6055SJesper Skriver 	 */
1128a75a485dSMike Silbersack 	if (maxnipq >= 0 && nipq > maxnipq) {
1129690a6055SJesper Skriver 		for (i = 0; i < IPREASS_NHASH; i++) {
1130b36f5b37SMaxim Konovalov 			while (nipq > maxnipq && !TAILQ_EMPTY(&ipq[i])) {
113199e8617dSMaxim Konovalov 				ipstat.ips_fragdropped +=
113299e8617dSMaxim Konovalov 				    TAILQ_FIRST(&ipq[i])->ipq_nfrags;
1133690a6055SJesper Skriver 				ip_freef(&ipq[i], TAILQ_FIRST(&ipq[i]));
1134690a6055SJesper Skriver 			}
1135690a6055SJesper Skriver 		}
1136690a6055SJesper Skriver 	}
11372fad1e93SSam Leffler 	IPQ_UNLOCK();
1138df8bae1dSRodney W. Grimes 	splx(s);
1139df8bae1dSRodney W. Grimes }
1140df8bae1dSRodney W. Grimes 
1141df8bae1dSRodney W. Grimes /*
1142df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
1143df8bae1dSRodney W. Grimes  */
1144df8bae1dSRodney W. Grimes void
1145df8bae1dSRodney W. Grimes ip_drain()
1146df8bae1dSRodney W. Grimes {
1147194a213eSAndrey A. Chernov 	int     i;
1148ce29ab3aSGarrett Wollman 
11492fad1e93SSam Leffler 	IPQ_LOCK();
1150194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
1151462b86feSPoul-Henning Kamp 		while(!TAILQ_EMPTY(&ipq[i])) {
115299e8617dSMaxim Konovalov 			ipstat.ips_fragdropped +=
115399e8617dSMaxim Konovalov 			    TAILQ_FIRST(&ipq[i])->ipq_nfrags;
1154462b86feSPoul-Henning Kamp 			ip_freef(&ipq[i], TAILQ_FIRST(&ipq[i]));
1155194a213eSAndrey A. Chernov 		}
1156194a213eSAndrey A. Chernov 	}
11572fad1e93SSam Leffler 	IPQ_UNLOCK();
1158ce29ab3aSGarrett Wollman 	in_rtqdrain();
1159df8bae1dSRodney W. Grimes }
1160df8bae1dSRodney W. Grimes 
1161df8bae1dSRodney W. Grimes /*
1162de38924dSAndre Oppermann  * The protocol to be inserted into ip_protox[] must be already registered
1163de38924dSAndre Oppermann  * in inetsw[], either statically or through pf_proto_register().
1164de38924dSAndre Oppermann  */
1165de38924dSAndre Oppermann int
1166de38924dSAndre Oppermann ipproto_register(u_char ipproto)
1167de38924dSAndre Oppermann {
1168de38924dSAndre Oppermann 	struct protosw *pr;
1169de38924dSAndre Oppermann 
1170de38924dSAndre Oppermann 	/* Sanity checks. */
1171de38924dSAndre Oppermann 	if (ipproto == 0)
1172de38924dSAndre Oppermann 		return (EPROTONOSUPPORT);
1173de38924dSAndre Oppermann 
1174de38924dSAndre Oppermann 	/*
1175de38924dSAndre Oppermann 	 * The protocol slot must not be occupied by another protocol
1176de38924dSAndre Oppermann 	 * already.  An index pointing to IPPROTO_RAW is unused.
1177de38924dSAndre Oppermann 	 */
1178de38924dSAndre Oppermann 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
1179de38924dSAndre Oppermann 	if (pr == NULL)
1180de38924dSAndre Oppermann 		return (EPFNOSUPPORT);
1181de38924dSAndre Oppermann 	if (ip_protox[ipproto] != pr - inetsw)	/* IPPROTO_RAW */
1182de38924dSAndre Oppermann 		return (EEXIST);
1183de38924dSAndre Oppermann 
1184de38924dSAndre Oppermann 	/* Find the protocol position in inetsw[] and set the index. */
1185de38924dSAndre Oppermann 	for (pr = inetdomain.dom_protosw;
1186de38924dSAndre Oppermann 	     pr < inetdomain.dom_protoswNPROTOSW; pr++) {
1187de38924dSAndre Oppermann 		if (pr->pr_domain->dom_family == PF_INET &&
1188de38924dSAndre Oppermann 		    pr->pr_protocol && pr->pr_protocol == ipproto) {
1189de38924dSAndre Oppermann 			/* Be careful to only index valid IP protocols. */
1190de38924dSAndre Oppermann 			if (pr->pr_protocol <= IPPROTO_MAX) {
1191de38924dSAndre Oppermann 				ip_protox[pr->pr_protocol] = pr - inetsw;
1192de38924dSAndre Oppermann 				return (0);
1193de38924dSAndre Oppermann 			} else
1194de38924dSAndre Oppermann 				return (EINVAL);
1195de38924dSAndre Oppermann 		}
1196de38924dSAndre Oppermann 	}
1197de38924dSAndre Oppermann 	return (EPROTONOSUPPORT);
1198de38924dSAndre Oppermann }
1199de38924dSAndre Oppermann 
1200de38924dSAndre Oppermann int
1201de38924dSAndre Oppermann ipproto_unregister(u_char ipproto)
1202de38924dSAndre Oppermann {
1203de38924dSAndre Oppermann 	struct protosw *pr;
1204de38924dSAndre Oppermann 
1205de38924dSAndre Oppermann 	/* Sanity checks. */
1206de38924dSAndre Oppermann 	if (ipproto == 0)
1207de38924dSAndre Oppermann 		return (EPROTONOSUPPORT);
1208de38924dSAndre Oppermann 
1209de38924dSAndre Oppermann 	/* Check if the protocol was indeed registered. */
1210de38924dSAndre Oppermann 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
1211de38924dSAndre Oppermann 	if (pr == NULL)
1212de38924dSAndre Oppermann 		return (EPFNOSUPPORT);
1213de38924dSAndre Oppermann 	if (ip_protox[ipproto] == pr - inetsw)  /* IPPROTO_RAW */
1214de38924dSAndre Oppermann 		return (ENOENT);
1215de38924dSAndre Oppermann 
1216de38924dSAndre Oppermann 	/* Reset the protocol slot to IPPROTO_RAW. */
1217de38924dSAndre Oppermann 	ip_protox[ipproto] = pr - inetsw;
1218de38924dSAndre Oppermann 	return (0);
1219de38924dSAndre Oppermann }
1220de38924dSAndre Oppermann 
1221de38924dSAndre Oppermann 
1222de38924dSAndre Oppermann /*
1223df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
1224df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
1225df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
1226d0ebc0d2SYaroslav Tykhiy  * The pass argument is used when operating in the IPSTEALTH
1227d0ebc0d2SYaroslav Tykhiy  * mode to tell what options to process:
1228d0ebc0d2SYaroslav Tykhiy  * [LS]SRR (pass 0) or the others (pass 1).
1229d0ebc0d2SYaroslav Tykhiy  * The reason for as many as two passes is that when doing IPSTEALTH,
1230d0ebc0d2SYaroslav Tykhiy  * non-routing options should be processed only if the packet is for us.
1231df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
1232df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
1233df8bae1dSRodney W. Grimes  */
12340312fbe9SPoul-Henning Kamp static int
12359b932e9eSAndre Oppermann ip_dooptions(struct mbuf *m, int pass)
1236df8bae1dSRodney W. Grimes {
12372b25acc1SLuigi Rizzo 	struct ip *ip = mtod(m, struct ip *);
12382b25acc1SLuigi Rizzo 	u_char *cp;
12392b25acc1SLuigi Rizzo 	struct in_ifaddr *ia;
1240df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
1241df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
1242df8bae1dSRodney W. Grimes 	n_time ntime;
12434d2e3692SLuigi Rizzo 	struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
1244df8bae1dSRodney W. Grimes 
12452bde81acSAndre Oppermann 	/* ignore or reject packets with IP options */
12462bde81acSAndre Oppermann 	if (ip_doopts == 0)
12472bde81acSAndre Oppermann 		return 0;
12482bde81acSAndre Oppermann 	else if (ip_doopts == 2) {
12492bde81acSAndre Oppermann 		type = ICMP_UNREACH;
12502bde81acSAndre Oppermann 		code = ICMP_UNREACH_FILTER_PROHIB;
12512bde81acSAndre Oppermann 		goto bad;
12522bde81acSAndre Oppermann 	}
12532bde81acSAndre Oppermann 
1254df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
1255df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
125653be11f6SPoul-Henning Kamp 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
1257df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
1258df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
1259df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
1260df8bae1dSRodney W. Grimes 			break;
1261df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
1262df8bae1dSRodney W. Grimes 			optlen = 1;
1263df8bae1dSRodney W. Grimes 		else {
1264fdcb8debSJun-ichiro itojun Hagino 			if (cnt < IPOPT_OLEN + sizeof(*cp)) {
1265fdcb8debSJun-ichiro itojun Hagino 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1266fdcb8debSJun-ichiro itojun Hagino 				goto bad;
1267fdcb8debSJun-ichiro itojun Hagino 			}
1268df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
1269707d00a3SJonathan Lemon 			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {
1270df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1271df8bae1dSRodney W. Grimes 				goto bad;
1272df8bae1dSRodney W. Grimes 			}
1273df8bae1dSRodney W. Grimes 		}
1274df8bae1dSRodney W. Grimes 		switch (opt) {
1275df8bae1dSRodney W. Grimes 
1276df8bae1dSRodney W. Grimes 		default:
1277df8bae1dSRodney W. Grimes 			break;
1278df8bae1dSRodney W. Grimes 
1279df8bae1dSRodney W. Grimes 		/*
1280df8bae1dSRodney W. Grimes 		 * Source routing with record.
1281df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
1282df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
1283df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
1284df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
1285df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
1286df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
1287df8bae1dSRodney W. Grimes 		 */
1288df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
1289df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
1290d0ebc0d2SYaroslav Tykhiy #ifdef IPSTEALTH
1291d0ebc0d2SYaroslav Tykhiy 			if (ipstealth && pass > 0)
1292d0ebc0d2SYaroslav Tykhiy 				break;
1293d0ebc0d2SYaroslav Tykhiy #endif
129433841545SHajimu UMEMOTO 			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
129533841545SHajimu UMEMOTO 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
129633841545SHajimu UMEMOTO 				goto bad;
129733841545SHajimu UMEMOTO 			}
1298df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1299df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1300df8bae1dSRodney W. Grimes 				goto bad;
1301df8bae1dSRodney W. Grimes 			}
1302df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
1303df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
1304df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
13050b17fba7SAndre Oppermann 			if (ia == NULL) {
1306df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
1307df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
1308df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
1309df8bae1dSRodney W. Grimes 					goto bad;
1310df8bae1dSRodney W. Grimes 				}
1311bc189bf8SGuido van Rooij 				if (!ip_dosourceroute)
1312bc189bf8SGuido van Rooij 					goto nosourcerouting;
1313df8bae1dSRodney W. Grimes 				/*
1314df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
1315df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
1316df8bae1dSRodney W. Grimes 				 */
1317df8bae1dSRodney W. Grimes 				break;
1318df8bae1dSRodney W. Grimes 			}
1319df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
13205d5d5fc0SJonathan Lemon 			if (off > optlen - (int)sizeof(struct in_addr)) {
1321df8bae1dSRodney W. Grimes 				/*
1322df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
1323df8bae1dSRodney W. Grimes 				 */
13244fce5804SGuido van Rooij 				if (!ip_acceptsourceroute)
13254fce5804SGuido van Rooij 					goto nosourcerouting;
1326e0982661SAndre Oppermann 				save_rte(m, cp, ip->ip_src);
1327df8bae1dSRodney W. Grimes 				break;
1328df8bae1dSRodney W. Grimes 			}
1329d0ebc0d2SYaroslav Tykhiy #ifdef IPSTEALTH
1330d0ebc0d2SYaroslav Tykhiy 			if (ipstealth)
1331d0ebc0d2SYaroslav Tykhiy 				goto dropit;
1332d0ebc0d2SYaroslav Tykhiy #endif
13331025071fSGarrett Wollman 			if (!ip_dosourceroute) {
13340af8d3ecSDavid Greenman 				if (ipforwarding) {
13350af8d3ecSDavid Greenman 					char buf[16]; /* aaa.bbb.ccc.ddd\0 */
13360af8d3ecSDavid Greenman 					/*
13370af8d3ecSDavid Greenman 					 * Acting as a router, so generate ICMP
13380af8d3ecSDavid Greenman 					 */
1339efa48587SGuido van Rooij nosourcerouting:
1340bc189bf8SGuido van Rooij 					strcpy(buf, inet_ntoa(ip->ip_dst));
13411025071fSGarrett Wollman 					log(LOG_WARNING,
13421025071fSGarrett Wollman 					    "attempted source route from %s to %s\n",
13431025071fSGarrett Wollman 					    inet_ntoa(ip->ip_src), buf);
13441025071fSGarrett Wollman 					type = ICMP_UNREACH;
13451025071fSGarrett Wollman 					code = ICMP_UNREACH_SRCFAIL;
13461025071fSGarrett Wollman 					goto bad;
13470af8d3ecSDavid Greenman 				} else {
13480af8d3ecSDavid Greenman 					/*
13490af8d3ecSDavid Greenman 					 * Not acting as a router, so silently drop.
13500af8d3ecSDavid Greenman 					 */
1351d0ebc0d2SYaroslav Tykhiy #ifdef IPSTEALTH
1352d0ebc0d2SYaroslav Tykhiy dropit:
1353d0ebc0d2SYaroslav Tykhiy #endif
13540af8d3ecSDavid Greenman 					ipstat.ips_cantforward++;
13550af8d3ecSDavid Greenman 					m_freem(m);
13560af8d3ecSDavid Greenman 					return (1);
13570af8d3ecSDavid Greenman 				}
13581025071fSGarrett Wollman 			}
13591025071fSGarrett Wollman 
1360df8bae1dSRodney W. Grimes 			/*
1361df8bae1dSRodney W. Grimes 			 * locate outgoing interface
1362df8bae1dSRodney W. Grimes 			 */
136394a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
1364df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
13651025071fSGarrett Wollman 
1366df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
1367df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
1368df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
13690b17fba7SAndre Oppermann 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == NULL)
1370df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
1371df8bae1dSRodney W. Grimes 			} else
137202c1c707SAndre Oppermann 				ia = ip_rtaddr(ipaddr.sin_addr);
13730b17fba7SAndre Oppermann 			if (ia == NULL) {
1374df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1375df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
1376df8bae1dSRodney W. Grimes 				goto bad;
1377df8bae1dSRodney W. Grimes 			}
1378df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
137994a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
138094a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1381df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1382df8bae1dSRodney W. Grimes 			/*
1383df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
1384df8bae1dSRodney W. Grimes 			 */
1385df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1386df8bae1dSRodney W. Grimes 			break;
1387df8bae1dSRodney W. Grimes 
1388df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1389d0ebc0d2SYaroslav Tykhiy #ifdef IPSTEALTH
1390d0ebc0d2SYaroslav Tykhiy 			if (ipstealth && pass == 0)
1391d0ebc0d2SYaroslav Tykhiy 				break;
1392d0ebc0d2SYaroslav Tykhiy #endif
1393707d00a3SJonathan Lemon 			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
1394707d00a3SJonathan Lemon 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1395707d00a3SJonathan Lemon 				goto bad;
1396707d00a3SJonathan Lemon 			}
1397df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1398df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1399df8bae1dSRodney W. Grimes 				goto bad;
1400df8bae1dSRodney W. Grimes 			}
1401df8bae1dSRodney W. Grimes 			/*
1402df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1403df8bae1dSRodney W. Grimes 			 */
1404df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
14055d5d5fc0SJonathan Lemon 			if (off > optlen - (int)sizeof(struct in_addr))
1406df8bae1dSRodney W. Grimes 				break;
140794a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1408df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1409df8bae1dSRodney W. Grimes 			/*
1410df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1411df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1412df8bae1dSRodney W. Grimes 			 */
14130b17fba7SAndre Oppermann 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == NULL &&
14140b17fba7SAndre Oppermann 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == NULL) {
1415df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1416df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1417df8bae1dSRodney W. Grimes 				goto bad;
1418df8bae1dSRodney W. Grimes 			}
141994a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
142094a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1421df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1422df8bae1dSRodney W. Grimes 			break;
1423df8bae1dSRodney W. Grimes 
1424df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1425d0ebc0d2SYaroslav Tykhiy #ifdef IPSTEALTH
1426d0ebc0d2SYaroslav Tykhiy 			if (ipstealth && pass == 0)
1427d0ebc0d2SYaroslav Tykhiy 				break;
1428d0ebc0d2SYaroslav Tykhiy #endif
1429df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
143007514071SJonathan Lemon 			if (optlen < 4 || optlen > 40) {
143107514071SJonathan Lemon 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1432df8bae1dSRodney W. Grimes 				goto bad;
143333841545SHajimu UMEMOTO 			}
143407514071SJonathan Lemon 			if ((off = cp[IPOPT_OFFSET]) < 5) {
143507514071SJonathan Lemon 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
143633841545SHajimu UMEMOTO 				goto bad;
143733841545SHajimu UMEMOTO 			}
143807514071SJonathan Lemon 			if (off > optlen - (int)sizeof(int32_t)) {
143907514071SJonathan Lemon 				cp[IPOPT_OFFSET + 1] += (1 << 4);
144007514071SJonathan Lemon 				if ((cp[IPOPT_OFFSET + 1] & 0xf0) == 0) {
144107514071SJonathan Lemon 					code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1442df8bae1dSRodney W. Grimes 					goto bad;
144333841545SHajimu UMEMOTO 				}
1444df8bae1dSRodney W. Grimes 				break;
1445df8bae1dSRodney W. Grimes 			}
144607514071SJonathan Lemon 			off--;				/* 0 origin */
144707514071SJonathan Lemon 			sin = (struct in_addr *)(cp + off);
144807514071SJonathan Lemon 			switch (cp[IPOPT_OFFSET + 1] & 0x0f) {
1449df8bae1dSRodney W. Grimes 
1450df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1451df8bae1dSRodney W. Grimes 				break;
1452df8bae1dSRodney W. Grimes 
1453df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
145407514071SJonathan Lemon 				if (off + sizeof(n_time) +
145507514071SJonathan Lemon 				    sizeof(struct in_addr) > optlen) {
145607514071SJonathan Lemon 					code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1457df8bae1dSRodney W. Grimes 					goto bad;
145833841545SHajimu UMEMOTO 				}
1459df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1460df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1461df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
14620b17fba7SAndre Oppermann 				if (ia == NULL)
1463df8bae1dSRodney W. Grimes 					continue;
146494a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
146594a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
146607514071SJonathan Lemon 				cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1467a5428e3aSMaxim Konovalov 				off += sizeof(struct in_addr);
1468df8bae1dSRodney W. Grimes 				break;
1469df8bae1dSRodney W. Grimes 
1470df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
147107514071SJonathan Lemon 				if (off + sizeof(n_time) +
147207514071SJonathan Lemon 				    sizeof(struct in_addr) > optlen) {
147307514071SJonathan Lemon 					code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1474df8bae1dSRodney W. Grimes 					goto bad;
147533841545SHajimu UMEMOTO 				}
147694a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1477df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
14780b17fba7SAndre Oppermann 				if (ifa_ifwithaddr((SA)&ipaddr) == NULL)
1479df8bae1dSRodney W. Grimes 					continue;
148007514071SJonathan Lemon 				cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1481a5428e3aSMaxim Konovalov 				off += sizeof(struct in_addr);
1482df8bae1dSRodney W. Grimes 				break;
1483df8bae1dSRodney W. Grimes 
1484df8bae1dSRodney W. Grimes 			default:
148507514071SJonathan Lemon 				code = &cp[IPOPT_OFFSET + 1] - (u_char *)ip;
1486df8bae1dSRodney W. Grimes 				goto bad;
1487df8bae1dSRodney W. Grimes 			}
1488df8bae1dSRodney W. Grimes 			ntime = iptime();
148907514071SJonathan Lemon 			(void)memcpy(cp + off, &ntime, sizeof(n_time));
149007514071SJonathan Lemon 			cp[IPOPT_OFFSET] += sizeof(n_time);
1491df8bae1dSRodney W. Grimes 		}
1492df8bae1dSRodney W. Grimes 	}
149347174b49SAndrey A. Chernov 	if (forward && ipforwarding) {
14949b932e9eSAndre Oppermann 		ip_forward(m, 1);
1495df8bae1dSRodney W. Grimes 		return (1);
1496df8bae1dSRodney W. Grimes 	}
1497df8bae1dSRodney W. Grimes 	return (0);
1498df8bae1dSRodney W. Grimes bad:
1499df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1500df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1501df8bae1dSRodney W. Grimes 	return (1);
1502df8bae1dSRodney W. Grimes }
1503df8bae1dSRodney W. Grimes 
1504df8bae1dSRodney W. Grimes /*
1505df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1506df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1507df8bae1dSRodney W. Grimes  */
1508bd714208SRuslan Ermilov struct in_ifaddr *
150902c1c707SAndre Oppermann ip_rtaddr(dst)
1510df8bae1dSRodney W. Grimes 	struct in_addr dst;
1511df8bae1dSRodney W. Grimes {
151297d8d152SAndre Oppermann 	struct route sro;
151302c1c707SAndre Oppermann 	struct sockaddr_in *sin;
151402c1c707SAndre Oppermann 	struct in_ifaddr *ifa;
1515df8bae1dSRodney W. Grimes 
15160cfbbe3bSAndre Oppermann 	bzero(&sro, sizeof(sro));
151797d8d152SAndre Oppermann 	sin = (struct sockaddr_in *)&sro.ro_dst;
1518df8bae1dSRodney W. Grimes 	sin->sin_family = AF_INET;
1519df8bae1dSRodney W. Grimes 	sin->sin_len = sizeof(*sin);
1520df8bae1dSRodney W. Grimes 	sin->sin_addr = dst;
152197d8d152SAndre Oppermann 	rtalloc_ign(&sro, RTF_CLONING);
1522df8bae1dSRodney W. Grimes 
152397d8d152SAndre Oppermann 	if (sro.ro_rt == NULL)
1524df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
152502c1c707SAndre Oppermann 
152697d8d152SAndre Oppermann 	ifa = ifatoia(sro.ro_rt->rt_ifa);
152797d8d152SAndre Oppermann 	RTFREE(sro.ro_rt);
152802c1c707SAndre Oppermann 	return ifa;
1529df8bae1dSRodney W. Grimes }
1530df8bae1dSRodney W. Grimes 
1531df8bae1dSRodney W. Grimes /*
1532df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1533df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1534df8bae1dSRodney W. Grimes  */
153537c84183SPoul-Henning Kamp static void
1536e0982661SAndre Oppermann save_rte(m, option, dst)
1537e0982661SAndre Oppermann 	struct mbuf *m;
1538df8bae1dSRodney W. Grimes 	u_char *option;
1539df8bae1dSRodney W. Grimes 	struct in_addr dst;
1540df8bae1dSRodney W. Grimes {
1541df8bae1dSRodney W. Grimes 	unsigned olen;
1542e0982661SAndre Oppermann 	struct ipopt_tag *opts;
1543e0982661SAndre Oppermann 
1544e0982661SAndre Oppermann 	opts = (struct ipopt_tag *)m_tag_get(PACKET_TAG_IPOPTIONS,
1545e0982661SAndre Oppermann 					sizeof(struct ipopt_tag), M_NOWAIT);
1546e0982661SAndre Oppermann 	if (opts == NULL)
1547e0982661SAndre Oppermann 		return;
1548df8bae1dSRodney W. Grimes 
1549df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1550df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1551df8bae1dSRodney W. Grimes 	if (ipprintfs)
1552df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1553df8bae1dSRodney W. Grimes #endif
1554e0982661SAndre Oppermann 	if (olen > sizeof(opts->ip_srcrt) - (1 + sizeof(dst)))
1555df8bae1dSRodney W. Grimes 		return;
1556e0982661SAndre Oppermann 	bcopy(option, opts->ip_srcrt.srcopt, olen);
1557e0982661SAndre Oppermann 	opts->ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1558e0982661SAndre Oppermann 	opts->ip_srcrt.dst = dst;
1559e0982661SAndre Oppermann 	m_tag_prepend(m, (struct m_tag *)opts);
1560df8bae1dSRodney W. Grimes }
1561df8bae1dSRodney W. Grimes 
1562df8bae1dSRodney W. Grimes /*
1563df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1564df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1565df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1566df8bae1dSRodney W. Grimes  */
1567df8bae1dSRodney W. Grimes struct mbuf *
1568e0982661SAndre Oppermann ip_srcroute(m0)
1569e0982661SAndre Oppermann 	struct mbuf *m0;
1570df8bae1dSRodney W. Grimes {
1571df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1572df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1573e0982661SAndre Oppermann 	struct ipopt_tag *opts;
1574df8bae1dSRodney W. Grimes 
1575e0982661SAndre Oppermann 	opts = (struct ipopt_tag *)m_tag_find(m0, PACKET_TAG_IPOPTIONS, NULL);
1576e0982661SAndre Oppermann 	if (opts == NULL)
1577e0982661SAndre Oppermann 		return ((struct mbuf *)0);
1578e0982661SAndre Oppermann 
1579e0982661SAndre Oppermann 	if (opts->ip_nhops == 0)
1580df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1581a163d034SWarner Losh 	m = m_get(M_DONTWAIT, MT_HEADER);
15820b17fba7SAndre Oppermann 	if (m == NULL)
1583df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1584df8bae1dSRodney W. Grimes 
1585e0982661SAndre Oppermann #define OPTSIZ	(sizeof(opts->ip_srcrt.nop) + sizeof(opts->ip_srcrt.srcopt))
1586df8bae1dSRodney W. Grimes 
1587df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1588e0982661SAndre Oppermann 	m->m_len = opts->ip_nhops * sizeof(struct in_addr) +
1589e0982661SAndre Oppermann 	    sizeof(struct in_addr) + OPTSIZ;
1590df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1591df8bae1dSRodney W. Grimes 	if (ipprintfs)
1592e0982661SAndre Oppermann 		printf("ip_srcroute: nhops %d mlen %d", opts->ip_nhops, m->m_len);
1593df8bae1dSRodney W. Grimes #endif
1594df8bae1dSRodney W. Grimes 
1595df8bae1dSRodney W. Grimes 	/*
1596df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1597df8bae1dSRodney W. Grimes 	 */
1598e0982661SAndre Oppermann 	p = &(opts->ip_srcrt.route[opts->ip_nhops - 1]);
1599df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1600df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1601df8bae1dSRodney W. Grimes 	if (ipprintfs)
1602af38c68cSLuigi Rizzo 		printf(" hops %lx", (u_long)ntohl(mtod(m, struct in_addr *)->s_addr));
1603df8bae1dSRodney W. Grimes #endif
1604df8bae1dSRodney W. Grimes 
1605df8bae1dSRodney W. Grimes 	/*
1606df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1607df8bae1dSRodney W. Grimes 	 */
1608e0982661SAndre Oppermann 	opts->ip_srcrt.nop = IPOPT_NOP;
1609e0982661SAndre Oppermann 	opts->ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
161094a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
1611e0982661SAndre Oppermann 	    &(opts->ip_srcrt.nop), OPTSIZ);
1612df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1613df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1614df8bae1dSRodney W. Grimes #undef OPTSIZ
1615df8bae1dSRodney W. Grimes 	/*
1616df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1617df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1618df8bae1dSRodney W. Grimes 	 */
1619e0982661SAndre Oppermann 	while (p >= opts->ip_srcrt.route) {
1620df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1621df8bae1dSRodney W. Grimes 		if (ipprintfs)
1622af38c68cSLuigi Rizzo 			printf(" %lx", (u_long)ntohl(q->s_addr));
1623df8bae1dSRodney W. Grimes #endif
1624df8bae1dSRodney W. Grimes 		*q++ = *p--;
1625df8bae1dSRodney W. Grimes 	}
1626df8bae1dSRodney W. Grimes 	/*
1627df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1628df8bae1dSRodney W. Grimes 	 */
1629e0982661SAndre Oppermann 	*q = opts->ip_srcrt.dst;
1630df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1631df8bae1dSRodney W. Grimes 	if (ipprintfs)
1632af38c68cSLuigi Rizzo 		printf(" %lx\n", (u_long)ntohl(q->s_addr));
1633df8bae1dSRodney W. Grimes #endif
1634e0982661SAndre Oppermann 	m_tag_delete(m0, (struct m_tag *)opts);
1635df8bae1dSRodney W. Grimes 	return (m);
1636df8bae1dSRodney W. Grimes }
1637df8bae1dSRodney W. Grimes 
1638df8bae1dSRodney W. Grimes /*
1639df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1640df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1641df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1642df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1643df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1644df8bae1dSRodney W. Grimes  */
1645df8bae1dSRodney W. Grimes void
1646df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1647df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1648df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1649df8bae1dSRodney W. Grimes {
1650df8bae1dSRodney W. Grimes 	register int i;
1651df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1652df8bae1dSRodney W. Grimes 	register caddr_t opts;
1653df8bae1dSRodney W. Grimes 	int olen;
1654df8bae1dSRodney W. Grimes 
165553be11f6SPoul-Henning Kamp 	olen = (ip->ip_hl << 2) - sizeof (struct ip);
1656df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1657df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1658df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1659df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1660df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1661df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
166253be11f6SPoul-Henning Kamp 	ip->ip_v = IPVERSION;
166353be11f6SPoul-Henning Kamp 	ip->ip_hl = sizeof(struct ip) >> 2;
1664df8bae1dSRodney W. Grimes }
1665df8bae1dSRodney W. Grimes 
1666df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1667df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1668df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1669df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1670df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1671fcaf9f91SMike Silbersack 	0,		0,		EHOSTUNREACH,	0,
16723b8123b7SJesper Skriver 	ENOPROTOOPT,	ECONNREFUSED
1673df8bae1dSRodney W. Grimes };
1674df8bae1dSRodney W. Grimes 
1675df8bae1dSRodney W. Grimes /*
1676df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1677df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1678df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1679df8bae1dSRodney W. Grimes  * of codes and types.
1680df8bae1dSRodney W. Grimes  *
1681df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1682df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1683df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1684df8bae1dSRodney W. Grimes  * protocol deal with that.
1685df8bae1dSRodney W. Grimes  *
1686df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1687df8bae1dSRodney W. Grimes  * via a source route.
1688df8bae1dSRodney W. Grimes  */
16899b932e9eSAndre Oppermann void
16909b932e9eSAndre Oppermann ip_forward(struct mbuf *m, int srcrt)
1691df8bae1dSRodney W. Grimes {
16922b25acc1SLuigi Rizzo 	struct ip *ip = mtod(m, struct ip *);
16939b932e9eSAndre Oppermann 	struct in_ifaddr *ia = NULL;
169426f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1695df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
16969b932e9eSAndre Oppermann 	struct in_addr dest;
16979b932e9eSAndre Oppermann 	struct ifnet *destifp, dummyifp;
16983efc3014SJulian Elischer 
1699df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1700df8bae1dSRodney W. Grimes 	if (ipprintfs)
170161ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
17029b932e9eSAndre Oppermann 		    (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr,
1703162886e2SBruce Evans 		    ip->ip_ttl);
1704df8bae1dSRodney W. Grimes #endif
1705100ba1a6SJordan K. Hubbard 
1706100ba1a6SJordan K. Hubbard 
17079b932e9eSAndre Oppermann 	if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) {
1708df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1709df8bae1dSRodney W. Grimes 		m_freem(m);
1710df8bae1dSRodney W. Grimes 		return;
1711df8bae1dSRodney W. Grimes 	}
17121b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
17131b968362SDag-Erling Smørgrav 	if (!ipstealth) {
17141b968362SDag-Erling Smørgrav #endif
1715df8bae1dSRodney W. Grimes 		if (ip->ip_ttl <= IPTTLDEC) {
17161b968362SDag-Erling Smørgrav 			icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
171702c1c707SAndre Oppermann 			    0, 0);
1718df8bae1dSRodney W. Grimes 			return;
1719df8bae1dSRodney W. Grimes 		}
17201b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
17211b968362SDag-Erling Smørgrav 	}
17221b968362SDag-Erling Smørgrav #endif
1723df8bae1dSRodney W. Grimes 
17249b932e9eSAndre Oppermann 	if (!srcrt && (ia = ip_rtaddr(ip->ip_dst)) == NULL) {
172502c1c707SAndre Oppermann 		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
1726df8bae1dSRodney W. Grimes 		return;
172702c1c707SAndre Oppermann 	}
1728df8bae1dSRodney W. Grimes 
1729df8bae1dSRodney W. Grimes 	/*
1730bfef7ed4SIan Dowse 	 * Save the IP header and at most 8 bytes of the payload,
1731bfef7ed4SIan Dowse 	 * in case we need to generate an ICMP message to the src.
1732bfef7ed4SIan Dowse 	 *
17334d2e3692SLuigi Rizzo 	 * XXX this can be optimized a lot by saving the data in a local
17344d2e3692SLuigi Rizzo 	 * buffer on the stack (72 bytes at most), and only allocating the
17354d2e3692SLuigi Rizzo 	 * mbuf if really necessary. The vast majority of the packets
17364d2e3692SLuigi Rizzo 	 * are forwarded without having to send an ICMP back (either
17374d2e3692SLuigi Rizzo 	 * because unnecessary, or because rate limited), so we are
17384d2e3692SLuigi Rizzo 	 * really we are wasting a lot of work here.
17394d2e3692SLuigi Rizzo 	 *
1740bfef7ed4SIan Dowse 	 * We don't use m_copy() because it might return a reference
1741bfef7ed4SIan Dowse 	 * to a shared cluster. Both this function and ip_output()
1742bfef7ed4SIan Dowse 	 * assume exclusive access to the IP header in `m', so any
1743bfef7ed4SIan Dowse 	 * data in a cluster may change before we reach icmp_error().
1744df8bae1dSRodney W. Grimes 	 */
1745a163d034SWarner Losh 	MGET(mcopy, M_DONTWAIT, m->m_type);
1746a163d034SWarner Losh 	if (mcopy != NULL && !m_dup_pkthdr(mcopy, m, M_DONTWAIT)) {
17479967cafcSSam Leffler 		/*
17489967cafcSSam Leffler 		 * It's probably ok if the pkthdr dup fails (because
17499967cafcSSam Leffler 		 * the deep copy of the tag chain failed), but for now
17509967cafcSSam Leffler 		 * be conservative and just discard the copy since
17519967cafcSSam Leffler 		 * code below may some day want the tags.
17529967cafcSSam Leffler 		 */
17539967cafcSSam Leffler 		m_free(mcopy);
17549967cafcSSam Leffler 		mcopy = NULL;
17559967cafcSSam Leffler 	}
1756bfef7ed4SIan Dowse 	if (mcopy != NULL) {
175753be11f6SPoul-Henning Kamp 		mcopy->m_len = imin((ip->ip_hl << 2) + 8,
1758bfef7ed4SIan Dowse 		    (int)ip->ip_len);
1759e6b0a570SBruce M Simpson 		mcopy->m_pkthdr.len = mcopy->m_len;
1760bfef7ed4SIan Dowse 		m_copydata(m, 0, mcopy->m_len, mtod(mcopy, caddr_t));
1761bfef7ed4SIan Dowse 	}
176204287599SRuslan Ermilov 
176304287599SRuslan Ermilov #ifdef IPSTEALTH
176404287599SRuslan Ermilov 	if (!ipstealth) {
176504287599SRuslan Ermilov #endif
176604287599SRuslan Ermilov 		ip->ip_ttl -= IPTTLDEC;
176704287599SRuslan Ermilov #ifdef IPSTEALTH
176804287599SRuslan Ermilov 	}
176904287599SRuslan Ermilov #endif
1770df8bae1dSRodney W. Grimes 
1771df8bae1dSRodney W. Grimes 	/*
1772df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1773df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1774df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1775df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1776df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1777df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1778df8bae1dSRodney W. Grimes 	 */
17799b932e9eSAndre Oppermann 	dest.s_addr = 0;
17809b932e9eSAndre Oppermann 	if (!srcrt && ipsendredirects && ia->ia_ifp == m->m_pkthdr.rcvif) {
178102c1c707SAndre Oppermann 		struct sockaddr_in *sin;
178202c1c707SAndre Oppermann 		struct route ro;
178302c1c707SAndre Oppermann 		struct rtentry *rt;
178402c1c707SAndre Oppermann 
17850cfbbe3bSAndre Oppermann 		bzero(&ro, sizeof(ro));
178602c1c707SAndre Oppermann 		sin = (struct sockaddr_in *)&ro.ro_dst;
178702c1c707SAndre Oppermann 		sin->sin_family = AF_INET;
178802c1c707SAndre Oppermann 		sin->sin_len = sizeof(*sin);
17899b932e9eSAndre Oppermann 		sin->sin_addr = ip->ip_dst;
179026d02ca7SAndre Oppermann 		rtalloc_ign(&ro, RTF_CLONING);
179102c1c707SAndre Oppermann 
179202c1c707SAndre Oppermann 		rt = ro.ro_rt;
179302c1c707SAndre Oppermann 
179402c1c707SAndre Oppermann 		if (rt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
17959b932e9eSAndre Oppermann 		    satosin(rt_key(rt))->sin_addr.s_addr != 0) {
1796df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1797df8bae1dSRodney W. Grimes 			u_long src = ntohl(ip->ip_src.s_addr);
1798df8bae1dSRodney W. Grimes 
1799df8bae1dSRodney W. Grimes 			if (RTA(rt) &&
1800df8bae1dSRodney W. Grimes 			    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1801df8bae1dSRodney W. Grimes 				if (rt->rt_flags & RTF_GATEWAY)
18029b932e9eSAndre Oppermann 					dest.s_addr = satosin(rt->rt_gateway)->sin_addr.s_addr;
1803df8bae1dSRodney W. Grimes 				else
18049b932e9eSAndre Oppermann 					dest.s_addr = ip->ip_dst.s_addr;
1805df8bae1dSRodney W. Grimes 				/* Router requirements says to only send host redirects */
1806df8bae1dSRodney W. Grimes 				type = ICMP_REDIRECT;
1807df8bae1dSRodney W. Grimes 				code = ICMP_REDIRECT_HOST;
1808df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1809df8bae1dSRodney W. Grimes 				if (ipprintfs)
18109b932e9eSAndre Oppermann 					printf("redirect (%d) to %lx\n", code, (u_long)dest.s_addr);
1811df8bae1dSRodney W. Grimes #endif
1812df8bae1dSRodney W. Grimes 			}
1813df8bae1dSRodney W. Grimes 		}
181402c1c707SAndre Oppermann 		if (rt)
181502c1c707SAndre Oppermann 			RTFREE(rt);
181602c1c707SAndre Oppermann 	}
1817df8bae1dSRodney W. Grimes 
181802c1c707SAndre Oppermann 	error = ip_output(m, (struct mbuf *)0, NULL, IP_FORWARDING, 0, NULL);
1819df8bae1dSRodney W. Grimes 	if (error)
1820df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1821df8bae1dSRodney W. Grimes 	else {
1822df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1823df8bae1dSRodney W. Grimes 		if (type)
1824df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1825df8bae1dSRodney W. Grimes 		else {
18269188b4a1SAndre Oppermann 			if (mcopy)
1827df8bae1dSRodney W. Grimes 				m_freem(mcopy);
1828df8bae1dSRodney W. Grimes 			return;
1829df8bae1dSRodney W. Grimes 		}
1830df8bae1dSRodney W. Grimes 	}
1831df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1832df8bae1dSRodney W. Grimes 		return;
1833df8bae1dSRodney W. Grimes 	destifp = NULL;
1834df8bae1dSRodney W. Grimes 
1835df8bae1dSRodney W. Grimes 	switch (error) {
1836df8bae1dSRodney W. Grimes 
1837df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1838df8bae1dSRodney W. Grimes 		/* type, code set above */
1839df8bae1dSRodney W. Grimes 		break;
1840df8bae1dSRodney W. Grimes 
1841df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1842df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1843df8bae1dSRodney W. Grimes 	case ENETDOWN:
1844df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1845df8bae1dSRodney W. Grimes 	default:
1846df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1847df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1848df8bae1dSRodney W. Grimes 		break;
1849df8bae1dSRodney W. Grimes 
1850df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1851df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1852df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
185302c1c707SAndre Oppermann #if defined(IPSEC) || defined(FAST_IPSEC)
18546a800098SYoshinobu Inoue 		/*
18556a800098SYoshinobu Inoue 		 * If the packet is routed over IPsec tunnel, tell the
18566a800098SYoshinobu Inoue 		 * originator the tunnel MTU.
18576a800098SYoshinobu Inoue 		 *	tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
18586a800098SYoshinobu Inoue 		 * XXX quickhack!!!
18596a800098SYoshinobu Inoue 		 */
186002c1c707SAndre Oppermann 		{
18616a800098SYoshinobu Inoue 			struct secpolicy *sp = NULL;
18626a800098SYoshinobu Inoue 			int ipsecerror;
18636a800098SYoshinobu Inoue 			int ipsechdr;
186402c1c707SAndre Oppermann 			struct route *ro;
18656a800098SYoshinobu Inoue 
186602c1c707SAndre Oppermann #ifdef IPSEC
18676a800098SYoshinobu Inoue 			sp = ipsec4_getpolicybyaddr(mcopy,
18686a800098SYoshinobu Inoue 						    IPSEC_DIR_OUTBOUND,
18696a800098SYoshinobu Inoue 						    IP_FORWARDING,
18706a800098SYoshinobu Inoue 						    &ipsecerror);
187102c1c707SAndre Oppermann #else /* FAST_IPSEC */
1872b9234fafSSam Leffler 			sp = ipsec_getpolicybyaddr(mcopy,
1873b9234fafSSam Leffler 						   IPSEC_DIR_OUTBOUND,
1874b9234fafSSam Leffler 						   IP_FORWARDING,
1875b9234fafSSam Leffler 						   &ipsecerror);
187602c1c707SAndre Oppermann #endif
187702c1c707SAndre Oppermann 			if (sp != NULL) {
1878b9234fafSSam Leffler 				/* count IPsec header size */
1879b9234fafSSam Leffler 				ipsechdr = ipsec4_hdrsiz(mcopy,
1880b9234fafSSam Leffler 							 IPSEC_DIR_OUTBOUND,
1881b9234fafSSam Leffler 							 NULL);
1882b9234fafSSam Leffler 
1883b9234fafSSam Leffler 				/*
1884b9234fafSSam Leffler 				 * find the correct route for outer IPv4
1885b9234fafSSam Leffler 				 * header, compute tunnel MTU.
1886b9234fafSSam Leffler 				 *
1887b9234fafSSam Leffler 				 * XXX BUG ALERT
1888b9234fafSSam Leffler 				 * The "dummyifp" code relies upon the fact
1889b9234fafSSam Leffler 				 * that icmp_error() touches only ifp->if_mtu.
1890b9234fafSSam Leffler 				 */
1891b9234fafSSam Leffler 				/*XXX*/
1892b9234fafSSam Leffler 				destifp = NULL;
1893b9234fafSSam Leffler 				if (sp->req != NULL
1894b9234fafSSam Leffler 				 && sp->req->sav != NULL
1895b9234fafSSam Leffler 				 && sp->req->sav->sah != NULL) {
189602c1c707SAndre Oppermann 					ro = &sp->req->sav->sah->sa_route;
189702c1c707SAndre Oppermann 					if (ro->ro_rt && ro->ro_rt->rt_ifp) {
1898b9234fafSSam Leffler 						dummyifp.if_mtu =
189957ab3660SBruce M Simpson 						    ro->ro_rt->rt_rmx.rmx_mtu ?
190057ab3660SBruce M Simpson 						    ro->ro_rt->rt_rmx.rmx_mtu :
190102c1c707SAndre Oppermann 						    ro->ro_rt->rt_ifp->if_mtu;
1902b9234fafSSam Leffler 						dummyifp.if_mtu -= ipsechdr;
1903b9234fafSSam Leffler 						destifp = &dummyifp;
1904b9234fafSSam Leffler 					}
1905b9234fafSSam Leffler 				}
1906b9234fafSSam Leffler 
190702c1c707SAndre Oppermann #ifdef IPSEC
190802c1c707SAndre Oppermann 				key_freesp(sp);
190902c1c707SAndre Oppermann #else /* FAST_IPSEC */
1910b9234fafSSam Leffler 				KEY_FREESP(&sp);
191102c1c707SAndre Oppermann #endif
191202c1c707SAndre Oppermann 				ipstat.ips_cantfrag++;
191302c1c707SAndre Oppermann 				break;
191402c1c707SAndre Oppermann 			} else
191502c1c707SAndre Oppermann #endif /*IPSEC || FAST_IPSEC*/
19169b932e9eSAndre Oppermann 		/*
19179b932e9eSAndre Oppermann 		 * When doing source routing 'ia' can be NULL.  Fall back
19189b932e9eSAndre Oppermann 		 * to the minimum guaranteed routeable packet size and use
19199b932e9eSAndre Oppermann 		 * the same hack as IPSEC to setup a dummyifp for icmp.
19209b932e9eSAndre Oppermann 		 */
19219b932e9eSAndre Oppermann 		if (ia == NULL) {
19229b932e9eSAndre Oppermann 			dummyifp.if_mtu = IP_MSS;
19239b932e9eSAndre Oppermann 			destifp = &dummyifp;
19249b932e9eSAndre Oppermann 		} else
192502c1c707SAndre Oppermann 			destifp = ia->ia_ifp;
192602c1c707SAndre Oppermann #if defined(IPSEC) || defined(FAST_IPSEC)
1927b9234fafSSam Leffler 		}
192802c1c707SAndre Oppermann #endif /*IPSEC || FAST_IPSEC*/
1929df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1930df8bae1dSRodney W. Grimes 		break;
1931df8bae1dSRodney W. Grimes 
1932df8bae1dSRodney W. Grimes 	case ENOBUFS:
1933df285b3dSMike Silbersack 		/*
1934df285b3dSMike Silbersack 		 * A router should not generate ICMP_SOURCEQUENCH as
1935df285b3dSMike Silbersack 		 * required in RFC1812 Requirements for IP Version 4 Routers.
1936df285b3dSMike Silbersack 		 * Source quench could be a big problem under DoS attacks,
1937df285b3dSMike Silbersack 		 * or if the underlying interface is rate-limited.
1938df285b3dSMike Silbersack 		 * Those who need source quench packets may re-enable them
1939df285b3dSMike Silbersack 		 * via the net.inet.ip.sendsourcequench sysctl.
1940df285b3dSMike Silbersack 		 */
1941df285b3dSMike Silbersack 		if (ip_sendsourcequench == 0) {
1942df285b3dSMike Silbersack 			m_freem(mcopy);
1943df285b3dSMike Silbersack 			return;
1944df285b3dSMike Silbersack 		} else {
1945df8bae1dSRodney W. Grimes 			type = ICMP_SOURCEQUENCH;
1946df8bae1dSRodney W. Grimes 			code = 0;
1947df285b3dSMike Silbersack 		}
1948df8bae1dSRodney W. Grimes 		break;
19493a06e3e0SRuslan Ermilov 
19503a06e3e0SRuslan Ermilov 	case EACCES:			/* ipfw denied packet */
19513a06e3e0SRuslan Ermilov 		m_freem(mcopy);
19523a06e3e0SRuslan Ermilov 		return;
1953df8bae1dSRodney W. Grimes 	}
19549b932e9eSAndre Oppermann 	icmp_error(mcopy, type, code, dest.s_addr, destifp);
1955df8bae1dSRodney W. Grimes }
1956df8bae1dSRodney W. Grimes 
195782c23ebaSBill Fenner void
195882c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
195982c23ebaSBill Fenner 	register struct inpcb *inp;
196082c23ebaSBill Fenner 	register struct mbuf **mp;
196182c23ebaSBill Fenner 	register struct ip *ip;
196282c23ebaSBill Fenner 	register struct mbuf *m;
196382c23ebaSBill Fenner {
1964be8a62e8SPoul-Henning Kamp 	if (inp->inp_socket->so_options & (SO_BINTIME | SO_TIMESTAMP)) {
1965be8a62e8SPoul-Henning Kamp 		struct bintime bt;
1966be8a62e8SPoul-Henning Kamp 
1967be8a62e8SPoul-Henning Kamp 		bintime(&bt);
1968be8a62e8SPoul-Henning Kamp 		if (inp->inp_socket->so_options & SO_BINTIME) {
1969be8a62e8SPoul-Henning Kamp 			*mp = sbcreatecontrol((caddr_t) &bt, sizeof(bt),
1970be8a62e8SPoul-Henning Kamp 			SCM_BINTIME, SOL_SOCKET);
1971be8a62e8SPoul-Henning Kamp 			if (*mp)
1972be8a62e8SPoul-Henning Kamp 				mp = &(*mp)->m_next;
1973be8a62e8SPoul-Henning Kamp 		}
197482c23ebaSBill Fenner 		if (inp->inp_socket->so_options & SO_TIMESTAMP) {
197582c23ebaSBill Fenner 			struct timeval tv;
197682c23ebaSBill Fenner 
1977be8a62e8SPoul-Henning Kamp 			bintime2timeval(&bt, &tv);
197882c23ebaSBill Fenner 			*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
197982c23ebaSBill Fenner 				SCM_TIMESTAMP, SOL_SOCKET);
198082c23ebaSBill Fenner 			if (*mp)
198182c23ebaSBill Fenner 				mp = &(*mp)->m_next;
19824cc20ab1SSeigo Tanimura 		}
1983be8a62e8SPoul-Henning Kamp 	}
198482c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
198582c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
198682c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
198782c23ebaSBill Fenner 		if (*mp)
198882c23ebaSBill Fenner 			mp = &(*mp)->m_next;
198982c23ebaSBill Fenner 	}
19904957466bSMatthew N. Dodd 	if (inp->inp_flags & INP_RECVTTL) {
19914957466bSMatthew N. Dodd 		*mp = sbcreatecontrol((caddr_t) &ip->ip_ttl,
19924957466bSMatthew N. Dodd 		    sizeof(u_char), IP_RECVTTL, IPPROTO_IP);
19934957466bSMatthew N. Dodd 		if (*mp)
19944957466bSMatthew N. Dodd 			mp = &(*mp)->m_next;
19954957466bSMatthew N. Dodd 	}
199682c23ebaSBill Fenner #ifdef notyet
199782c23ebaSBill Fenner 	/* XXX
199882c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
199982c23ebaSBill Fenner 	 * than they already were.
200082c23ebaSBill Fenner 	 */
200182c23ebaSBill Fenner 	/* options were tossed already */
200282c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
200382c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
200482c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
200582c23ebaSBill Fenner 		if (*mp)
200682c23ebaSBill Fenner 			mp = &(*mp)->m_next;
200782c23ebaSBill Fenner 	}
200882c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
200982c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
2010e0982661SAndre Oppermann 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(m),
201182c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
201282c23ebaSBill Fenner 		if (*mp)
201382c23ebaSBill Fenner 			mp = &(*mp)->m_next;
201482c23ebaSBill Fenner 	}
201582c23ebaSBill Fenner #endif
201682c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
2017d314ad7bSJulian Elischer 		struct ifnet *ifp;
2018d314ad7bSJulian Elischer 		struct sdlbuf {
201982c23ebaSBill Fenner 			struct sockaddr_dl sdl;
2020d314ad7bSJulian Elischer 			u_char	pad[32];
2021d314ad7bSJulian Elischer 		} sdlbuf;
2022d314ad7bSJulian Elischer 		struct sockaddr_dl *sdp;
2023d314ad7bSJulian Elischer 		struct sockaddr_dl *sdl2 = &sdlbuf.sdl;
202482c23ebaSBill Fenner 
2025d314ad7bSJulian Elischer 		if (((ifp = m->m_pkthdr.rcvif))
2026d314ad7bSJulian Elischer 		&& ( ifp->if_index && (ifp->if_index <= if_index))) {
2027f9132cebSJonathan Lemon 			sdp = (struct sockaddr_dl *)
2028f9132cebSJonathan Lemon 			    (ifaddr_byindex(ifp->if_index)->ifa_addr);
2029d314ad7bSJulian Elischer 			/*
2030d314ad7bSJulian Elischer 			 * Change our mind and don't try copy.
2031d314ad7bSJulian Elischer 			 */
2032d314ad7bSJulian Elischer 			if ((sdp->sdl_family != AF_LINK)
2033d314ad7bSJulian Elischer 			|| (sdp->sdl_len > sizeof(sdlbuf))) {
2034d314ad7bSJulian Elischer 				goto makedummy;
2035d314ad7bSJulian Elischer 			}
2036d314ad7bSJulian Elischer 			bcopy(sdp, sdl2, sdp->sdl_len);
2037d314ad7bSJulian Elischer 		} else {
2038d314ad7bSJulian Elischer makedummy:
2039d314ad7bSJulian Elischer 			sdl2->sdl_len
2040d314ad7bSJulian Elischer 				= offsetof(struct sockaddr_dl, sdl_data[0]);
2041d314ad7bSJulian Elischer 			sdl2->sdl_family = AF_LINK;
2042d314ad7bSJulian Elischer 			sdl2->sdl_index = 0;
2043d314ad7bSJulian Elischer 			sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0;
2044d314ad7bSJulian Elischer 		}
2045d314ad7bSJulian Elischer 		*mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len,
204682c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
204782c23ebaSBill Fenner 		if (*mp)
204882c23ebaSBill Fenner 			mp = &(*mp)->m_next;
204982c23ebaSBill Fenner 	}
205082c23ebaSBill Fenner }
205182c23ebaSBill Fenner 
20524d2e3692SLuigi Rizzo /*
20534d2e3692SLuigi Rizzo  * XXX these routines are called from the upper part of the kernel.
20544d2e3692SLuigi Rizzo  * They need to be locked when we remove Giant.
20554d2e3692SLuigi Rizzo  *
20564d2e3692SLuigi Rizzo  * They could also be moved to ip_mroute.c, since all the RSVP
20574d2e3692SLuigi Rizzo  *  handling is done there already.
20584d2e3692SLuigi Rizzo  */
20594d2e3692SLuigi Rizzo static int ip_rsvp_on;
20604d2e3692SLuigi Rizzo struct socket *ip_rsvpd;
2061df8bae1dSRodney W. Grimes int
2062f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
2063f0068c4aSGarrett Wollman {
2064f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
2065f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
2066f0068c4aSGarrett Wollman 		return EOPNOTSUPP;
2067f0068c4aSGarrett Wollman 
2068f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
2069f0068c4aSGarrett Wollman 		return EADDRINUSE;
2070f0068c4aSGarrett Wollman 
2071f0068c4aSGarrett Wollman 	ip_rsvpd = so;
20721c5de19aSGarrett Wollman 	/*
20731c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
20741c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
20751c5de19aSGarrett Wollman 	 */
20761c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
20771c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
20781c5de19aSGarrett Wollman 		rsvp_on++;
20791c5de19aSGarrett Wollman 	}
2080f0068c4aSGarrett Wollman 
2081f0068c4aSGarrett Wollman 	return 0;
2082f0068c4aSGarrett Wollman }
2083f0068c4aSGarrett Wollman 
2084f0068c4aSGarrett Wollman int
2085f0068c4aSGarrett Wollman ip_rsvp_done(void)
2086f0068c4aSGarrett Wollman {
2087f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
20881c5de19aSGarrett Wollman 	/*
20891c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
20901c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
20911c5de19aSGarrett Wollman 	 */
20921c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
20931c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
20941c5de19aSGarrett Wollman 		rsvp_on--;
20951c5de19aSGarrett Wollman 	}
2096f0068c4aSGarrett Wollman 	return 0;
2097f0068c4aSGarrett Wollman }
2098bbb4330bSLuigi Rizzo 
2099bbb4330bSLuigi Rizzo void
2100bbb4330bSLuigi Rizzo rsvp_input(struct mbuf *m, int off)	/* XXX must fixup manually */
2101bbb4330bSLuigi Rizzo {
2102bbb4330bSLuigi Rizzo 	if (rsvp_input_p) { /* call the real one if loaded */
2103bbb4330bSLuigi Rizzo 		rsvp_input_p(m, off);
2104bbb4330bSLuigi Rizzo 		return;
2105bbb4330bSLuigi Rizzo 	}
2106bbb4330bSLuigi Rizzo 
2107bbb4330bSLuigi Rizzo 	/* Can still get packets with rsvp_on = 0 if there is a local member
2108bbb4330bSLuigi Rizzo 	 * of the group to which the RSVP packet is addressed.  But in this
2109bbb4330bSLuigi Rizzo 	 * case we want to throw the packet away.
2110bbb4330bSLuigi Rizzo 	 */
2111bbb4330bSLuigi Rizzo 
2112bbb4330bSLuigi Rizzo 	if (!rsvp_on) {
2113bbb4330bSLuigi Rizzo 		m_freem(m);
2114bbb4330bSLuigi Rizzo 		return;
2115bbb4330bSLuigi Rizzo 	}
2116bbb4330bSLuigi Rizzo 
2117bbb4330bSLuigi Rizzo 	if (ip_rsvpd != NULL) {
2118bbb4330bSLuigi Rizzo 		rip_input(m, off);
2119bbb4330bSLuigi Rizzo 		return;
2120bbb4330bSLuigi Rizzo 	}
2121bbb4330bSLuigi Rizzo 	/* Drop the packet */
2122bbb4330bSLuigi Rizzo 	m_freem(m);
2123bbb4330bSLuigi Rizzo }
2124