xref: /freebsd/sys/netinet/ip_input.c (revision f9e354df42e8d939ec8114b373b7c416c11a20bd)
1df8bae1dSRodney W. Grimes /*
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  * 3. All advertising materials mentioning features or use of this software
14df8bae1dSRodney W. Grimes  *    must display the following acknowledgement:
15df8bae1dSRodney W. Grimes  *	This product includes software developed by the University of
16df8bae1dSRodney W. Grimes  *	California, Berkeley and its contributors.
17df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
18df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
19df8bae1dSRodney W. Grimes  *    without specific prior written permission.
20df8bae1dSRodney W. Grimes  *
21df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
32df8bae1dSRodney W. Grimes  *
33df8bae1dSRodney W. Grimes  *	@(#)ip_input.c	8.2 (Berkeley) 1/4/94
34f9e354dfSJulian Elischer  * $Id: ip_input.c,v 1.91 1998/07/02 05:49:12 julian Exp $
3558938916SGarrett Wollman  *	$ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $
36df8bae1dSRodney W. Grimes  */
37df8bae1dSRodney W. Grimes 
3858938916SGarrett Wollman #define	_IP_VHL
3958938916SGarrett Wollman 
40e4f4247aSEivind Eklund #include "opt_bootp.h"
4174a9466cSGary Palmer #include "opt_ipfw.h"
42fbd1372aSJoerg Wunsch #include "opt_ipdivert.h"
431ee25934SPeter Wemm #include "opt_ipfilter.h"
4474a9466cSGary Palmer 
4582c23ebaSBill Fenner #include <stddef.h>
4682c23ebaSBill Fenner 
47df8bae1dSRodney W. Grimes #include <sys/param.h>
48df8bae1dSRodney W. Grimes #include <sys/systm.h>
49df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
50df8bae1dSRodney W. Grimes #include <sys/domain.h>
51df8bae1dSRodney W. Grimes #include <sys/protosw.h>
52df8bae1dSRodney W. Grimes #include <sys/socket.h>
53df8bae1dSRodney W. Grimes #include <sys/time.h>
54df8bae1dSRodney W. Grimes #include <sys/kernel.h>
551025071fSGarrett Wollman #include <sys/syslog.h>
56b5e8ce9fSBruce Evans #include <sys/sysctl.h>
57df8bae1dSRodney W. Grimes 
58df8bae1dSRodney W. Grimes #include <net/if.h>
59d314ad7bSJulian Elischer #include <net/if_var.h>
6082c23ebaSBill Fenner #include <net/if_dl.h>
61df8bae1dSRodney W. Grimes #include <net/route.h>
62748e0b0aSGarrett Wollman #include <net/netisr.h>
63df8bae1dSRodney W. Grimes 
64df8bae1dSRodney W. Grimes #include <netinet/in.h>
65df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
66b5e8ce9fSBruce Evans #include <netinet/in_var.h>
67df8bae1dSRodney W. Grimes #include <netinet/ip.h>
68df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
69df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
70df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
7158938916SGarrett Wollman #include <machine/in_cksum.h>
72df8bae1dSRodney W. Grimes 
73f0068c4aSGarrett Wollman #include <sys/socketvar.h>
746ddbf1e2SGary Palmer 
756ddbf1e2SGary Palmer #ifdef IPFIREWALL
766ddbf1e2SGary Palmer #include <netinet/ip_fw.h>
776ddbf1e2SGary Palmer #endif
786ddbf1e2SGary Palmer 
791c5de19aSGarrett Wollman int rsvp_on = 0;
80f708ef1bSPoul-Henning Kamp static int ip_rsvp_on;
81f0068c4aSGarrett Wollman struct socket *ip_rsvpd;
82f0068c4aSGarrett Wollman 
831f91d8c5SDavid Greenman int	ipforwarding = 0;
840312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW,
850312fbe9SPoul-Henning Kamp 	&ipforwarding, 0, "");
860312fbe9SPoul-Henning Kamp 
87d4fb926cSGarrett Wollman static int	ipsendredirects = 1; /* XXX */
880312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW,
890312fbe9SPoul-Henning Kamp 	&ipsendredirects, 0, "");
900312fbe9SPoul-Henning Kamp 
91df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
920312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW,
930312fbe9SPoul-Henning Kamp 	&ip_defttl, 0, "");
940312fbe9SPoul-Henning Kamp 
950312fbe9SPoul-Henning Kamp static int	ip_dosourceroute = 0;
960312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
970312fbe9SPoul-Henning Kamp 	&ip_dosourceroute, 0, "");
984fce5804SGuido van Rooij 
994fce5804SGuido van Rooij static int	ip_acceptsourceroute = 0;
1004fce5804SGuido van Rooij SYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute,
1014fce5804SGuido van Rooij 	CTLFLAG_RW, &ip_acceptsourceroute, 0, "");
102df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1030312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
104df8bae1dSRodney W. Grimes #endif
105df8bae1dSRodney W. Grimes 
106df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
107df8bae1dSRodney W. Grimes extern	struct protosw inetsw[];
108df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
1090312fbe9SPoul-Henning Kamp static int	ipqmaxlen = IFQ_MAXLEN;
11059562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; /* first inet address */
111df8bae1dSRodney W. Grimes struct	ifqueue ipintrq;
1120312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RD,
1130312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_maxlen, 0, "");
1140312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1150312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_drops, 0, "");
116df8bae1dSRodney W. Grimes 
117f23b4c91SGarrett Wollman struct ipstat ipstat;
1186fce01c9SGarrett Wollman SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RD,
1196fce01c9SGarrett Wollman 	&ipstat, ipstat, "");
120194a213eSAndrey A. Chernov 
121194a213eSAndrey A. Chernov /* Packet reassembly stuff */
122194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
123194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
124194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
125194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
126194a213eSAndrey A. Chernov 	((((x) & 0xF | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
127194a213eSAndrey A. Chernov 
128194a213eSAndrey A. Chernov static struct ipq ipq[IPREASS_NHASH];
129194a213eSAndrey A. Chernov static int    nipq = 0;         /* total # of reass queues */
130194a213eSAndrey A. Chernov static int    maxnipq;
131f23b4c91SGarrett Wollman 
1320312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1330312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1340312fbe9SPoul-Henning Kamp 	&ip_mtu, 0, "");
1350312fbe9SPoul-Henning Kamp #endif
1360312fbe9SPoul-Henning Kamp 
13758938916SGarrett Wollman #if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1
13858938916SGarrett Wollman #undef COMPAT_IPFW
13958938916SGarrett Wollman #define COMPAT_IPFW 1
14058938916SGarrett Wollman #else
14158938916SGarrett Wollman #undef COMPAT_IPFW
14258938916SGarrett Wollman #endif
14358938916SGarrett Wollman 
14458938916SGarrett Wollman #ifdef COMPAT_IPFW
14523bf9953SPoul-Henning Kamp /* Firewall hooks */
14623bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
14723bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
148e7319babSPoul-Henning Kamp 
149fed1c7e9SSøren Schmidt /* IP Network Address Translation (NAT) hooks */
150fed1c7e9SSøren Schmidt ip_nat_t *ip_nat_ptr;
151fed1c7e9SSøren Schmidt ip_nat_ctl_t *ip_nat_ctl_ptr;
15258938916SGarrett Wollman #endif
153fed1c7e9SSøren Schmidt 
154afed1b49SDarren Reed #if defined(IPFILTER_LKM) || defined(IPFILTER)
1551ee25934SPeter Wemm int iplattach __P((void));
156afed1b49SDarren Reed int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)) = NULL;
157afed1b49SDarren Reed #endif
158afed1b49SDarren Reed 
159afed1b49SDarren Reed 
160e7319babSPoul-Henning Kamp /*
161df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
162df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
163df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
164df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
165df8bae1dSRodney W. Grimes  * to us.
166df8bae1dSRodney W. Grimes  */
1670312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
168df8bae1dSRodney W. Grimes static	struct ip_srcrt {
169df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
170df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
171df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
172df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
173df8bae1dSRodney W. Grimes } ip_srcrt;
174df8bae1dSRodney W. Grimes 
17593e0e116SJulian Elischer #ifdef IPDIVERT
17693e0e116SJulian Elischer /*
17793e0e116SJulian Elischer  * Shared variable between ip_input() and ip_reass() to communicate
17893e0e116SJulian Elischer  * about which packets, once assembled from fragments, get diverted,
17993e0e116SJulian Elischer  * and to which port.
18093e0e116SJulian Elischer  */
18193e0e116SJulian Elischer static u_short	frag_divert_port;
18293e0e116SJulian Elischer #endif
18393e0e116SJulian Elischer 
184f9e354dfSJulian Elischer struct sockaddr_in *ip_fw_fwd_addr;
185f9e354dfSJulian Elischer 
186df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr));
1870312fbe9SPoul-Henning Kamp static void	 ip_deq __P((struct ipasfrag *));
1880312fbe9SPoul-Henning Kamp static int	 ip_dooptions __P((struct mbuf *));
1890312fbe9SPoul-Henning Kamp static void	 ip_enq __P((struct ipasfrag *, struct ipasfrag *));
1900312fbe9SPoul-Henning Kamp static void	 ip_forward __P((struct mbuf *, int));
1910312fbe9SPoul-Henning Kamp static void	 ip_freef __P((struct ipq *));
1920312fbe9SPoul-Henning Kamp static struct ip *
193194a213eSAndrey A. Chernov 	 ip_reass __P((struct ipasfrag *, struct ipq *, struct ipq *));
1940312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1950312fbe9SPoul-Henning Kamp 	 ip_rtaddr __P((struct in_addr));
1960312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
197df8bae1dSRodney W. Grimes /*
198df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
199df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
200df8bae1dSRodney W. Grimes  */
201df8bae1dSRodney W. Grimes void
202df8bae1dSRodney W. Grimes ip_init()
203df8bae1dSRodney W. Grimes {
204df8bae1dSRodney W. Grimes 	register struct protosw *pr;
205df8bae1dSRodney W. Grimes 	register int i;
206df8bae1dSRodney W. Grimes 
20759562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
208df8bae1dSRodney W. Grimes 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
209df8bae1dSRodney W. Grimes 	if (pr == 0)
210df8bae1dSRodney W. Grimes 		panic("ip_init");
211df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
212df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
213df8bae1dSRodney W. Grimes 	for (pr = inetdomain.dom_protosw;
214df8bae1dSRodney W. Grimes 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
215df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
216df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
217df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
218194a213eSAndrey A. Chernov 
219194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
220194a213eSAndrey A. Chernov 	    ipq[i].next = ipq[i].prev = &ipq[i];
221194a213eSAndrey A. Chernov 
222194a213eSAndrey A. Chernov 	maxnipq = nmbclusters/4;
223194a213eSAndrey A. Chernov 
224227ee8a1SPoul-Henning Kamp 	ip_id = time_second & 0xffff;
225df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
226b83e4314SPoul-Henning Kamp #ifdef IPFIREWALL
227b83e4314SPoul-Henning Kamp 	ip_fw_init();
228b83e4314SPoul-Henning Kamp #endif
229fed1c7e9SSøren Schmidt #ifdef IPNAT
230fed1c7e9SSøren Schmidt         ip_nat_init();
231fed1c7e9SSøren Schmidt #endif
2321ee25934SPeter Wemm #ifdef IPFILTER
2331ee25934SPeter Wemm         iplattach();
2341ee25934SPeter Wemm #endif
235fed1c7e9SSøren Schmidt 
236df8bae1dSRodney W. Grimes }
237df8bae1dSRodney W. Grimes 
2380312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
239f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
240df8bae1dSRodney W. Grimes 
241df8bae1dSRodney W. Grimes /*
242df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
243df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
244df8bae1dSRodney W. Grimes  */
245c67b1d17SGarrett Wollman void
246c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
247df8bae1dSRodney W. Grimes {
24823bf9953SPoul-Henning Kamp 	struct ip *ip;
24923bf9953SPoul-Henning Kamp 	struct ipq *fp;
25023bf9953SPoul-Henning Kamp 	struct in_ifaddr *ia;
251194a213eSAndrey A. Chernov 	int    i, hlen;
25247c861ecSBrian Somers 	u_short sum;
253df8bae1dSRodney W. Grimes 
254df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
255ed7509acSJulian Elischer 	if (m == NULL || (m->m_flags & M_PKTHDR) == 0)
25658938916SGarrett Wollman 		panic("ip_input no HDR");
257df8bae1dSRodney W. Grimes #endif
258df8bae1dSRodney W. Grimes 	/*
259df8bae1dSRodney W. Grimes 	 * If no IP addresses have been set yet but the interfaces
260df8bae1dSRodney W. Grimes 	 * are receiving, can't do anything with incoming packets yet.
26159562606SGarrett Wollman 	 * XXX This is broken! We should be able to receive broadcasts
26259562606SGarrett Wollman 	 * and multicasts even without any local addresses configured.
263df8bae1dSRodney W. Grimes 	 */
26459562606SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead))
265df8bae1dSRodney W. Grimes 		goto bad;
266df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
26758938916SGarrett Wollman 
26858938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
26958938916SGarrett Wollman 		goto tooshort;
27058938916SGarrett Wollman 
271df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
272df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
273df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
274c67b1d17SGarrett Wollman 		return;
275df8bae1dSRodney W. Grimes 	}
276df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
27758938916SGarrett Wollman 
27858938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
279df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
280df8bae1dSRodney W. Grimes 		goto bad;
281df8bae1dSRodney W. Grimes 	}
28258938916SGarrett Wollman 
28358938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
284df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
285df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
286df8bae1dSRodney W. Grimes 		goto bad;
287df8bae1dSRodney W. Grimes 	}
288df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
289df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
290df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
291c67b1d17SGarrett Wollman 			return;
292df8bae1dSRodney W. Grimes 		}
293df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
294df8bae1dSRodney W. Grimes 	}
29558938916SGarrett Wollman 	if (hlen == sizeof(struct ip)) {
29647c861ecSBrian Somers 		sum = in_cksum_hdr(ip);
29758938916SGarrett Wollman 	} else {
29847c861ecSBrian Somers 		sum = in_cksum(m, hlen);
29958938916SGarrett Wollman 	}
30047c861ecSBrian Somers 	if (sum) {
301df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
302df8bae1dSRodney W. Grimes 		goto bad;
303df8bae1dSRodney W. Grimes 	}
304df8bae1dSRodney W. Grimes 
305df8bae1dSRodney W. Grimes 	/*
306df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
307df8bae1dSRodney W. Grimes 	 */
308df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
309df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
310df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
311df8bae1dSRodney W. Grimes 		goto bad;
312df8bae1dSRodney W. Grimes 	}
313df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_id);
314df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
315df8bae1dSRodney W. Grimes 
316df8bae1dSRodney W. Grimes 	/*
317df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
318df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
319df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
320df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
321df8bae1dSRodney W. Grimes 	 */
322df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
32358938916SGarrett Wollman tooshort:
324df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
325df8bae1dSRodney W. Grimes 		goto bad;
326df8bae1dSRodney W. Grimes 	}
327df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
328df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
329df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
330df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
331df8bae1dSRodney W. Grimes 		} else
332df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
333df8bae1dSRodney W. Grimes 	}
3344dd1662bSUgen J.S. Antsilevich 	/*
3354dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3364dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
3374dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
3384dd1662bSUgen J.S. Antsilevich 	 * deals with it.
33993e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
340fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
3414dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
3424dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
3434dd1662bSUgen J.S. Antsilevich  	 */
344beec8214SDarren Reed #if defined(IPFILTER) || defined(IPFILTER_LKM)
345beec8214SDarren Reed 	/*
346beec8214SDarren Reed 	 * Check if we want to allow this packet to be processed.
347beec8214SDarren Reed 	 * Consider it to be bad if not.
348beec8214SDarren Reed 	 */
3491ee25934SPeter Wemm 	if (fr_checkp) {
350beec8214SDarren Reed 		struct	mbuf	*m1 = m;
351df8bae1dSRodney W. Grimes 
352beec8214SDarren Reed 		if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m1) || !m1)
353beec8214SDarren Reed 			return;
354beec8214SDarren Reed 		ip = mtod(m = m1, struct ip *);
355beec8214SDarren Reed 	}
356beec8214SDarren Reed #endif
35758938916SGarrett Wollman #ifdef COMPAT_IPFW
35893e0e116SJulian Elischer 	if (ip_fw_chk_ptr) {
359f9e354dfSJulian Elischer 		u_int16_t	port;
36093e0e116SJulian Elischer 
361f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
362f9e354dfSJulian Elischer 		/*
363f9e354dfSJulian Elischer 		 * If we've been forwarded from the output side, then
364f9e354dfSJulian Elischer 		 * skip the firewall a second time
365f9e354dfSJulian Elischer 		 */
366f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr)
367f9e354dfSJulian Elischer 			goto ours;
368f9e354dfSJulian Elischer #endif	/* IPFIREWALL_FORWARD */
369f9e354dfSJulian Elischer #ifdef IPDIVERT
370f9e354dfSJulian Elischer 		port = (*ip_fw_chk_ptr)(&ip, hlen, NULL, &ip_divert_cookie,
371f9e354dfSJulian Elischer 					&m, &ip_fw_fwd_addr);
372b3adeeb2SJulian Elischer 		if (port) {
373b3adeeb2SJulian Elischer 			/* Divert packet */
374e4676ba6SJulian Elischer 			frag_divert_port = port;
37593e0e116SJulian Elischer 			goto ours;
37693e0e116SJulian Elischer 		}
377f9e354dfSJulian Elischer #else	/* !DIVERT */
378f9e354dfSJulian Elischer 		/*
379f9e354dfSJulian Elischer 		 * If ipfw says divert, we have to just drop packet */
380f9e354dfSJulian Elischer 		 *  Use port as a dummy argument.
381f9e354dfSJulian Elischer 		 */
382f9e354dfSJulian Elischer 		port = 0;
383f9e354dfSJulian Elischer 		if ((*ip_fw_chk_ptr)(&ip, hlen, NULL, &port,
384f9e354dfSJulian Elischer 					&m, &ip_fw_fwd_addr)) {
385e4676ba6SJulian Elischer 			m_freem(m);
386e4676ba6SJulian Elischer 			m = NULL;
387e4676ba6SJulian Elischer 		}
388f9e354dfSJulian Elischer #endif	/* !DIVERT */
389e4676ba6SJulian Elischer 		if (!m)
390e4676ba6SJulian Elischer 			return;
39193e0e116SJulian Elischer 	}
392100ba1a6SJordan K. Hubbard 
3936713d4a7SSøren Schmidt         if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN))
394fed1c7e9SSøren Schmidt 		return;
395f9e354dfSJulian Elischer #endif	/* !COMPAT_IPFW */
396fed1c7e9SSøren Schmidt 
397df8bae1dSRodney W. Grimes 	/*
398df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
399df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
400df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
401df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
402df8bae1dSRodney W. Grimes 	 */
403df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
404df8bae1dSRodney W. Grimes 	if (hlen > sizeof (struct ip) && ip_dooptions(m))
405c67b1d17SGarrett Wollman 		return;
406df8bae1dSRodney W. Grimes 
407f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
408f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
409f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
410f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
411f0068c4aSGarrett Wollman 	 * grabbing the packet.
412f0068c4aSGarrett Wollman          */
4131c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
414f0068c4aSGarrett Wollman 		goto ours;
415f0068c4aSGarrett Wollman 
416df8bae1dSRodney W. Grimes 	/*
417df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
418df8bae1dSRodney W. Grimes 	 */
419f9e354dfSJulian Elischer 	for (ia = TAILQ_FIRST(in_ifaddrhead); ia;
420f9e354dfSJulian Elischer 					ia = TAILQ_NEXT(ia, ia_link)) {
421df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
422df8bae1dSRodney W. Grimes 
423df8bae1dSRodney W. Grimes 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
424df8bae1dSRodney W. Grimes 			goto ours;
425432aad0eSTor Egge #ifdef BOOTP_COMPAT
426432aad0eSTor Egge 		if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
427432aad0eSTor Egge 			goto ours;
428432aad0eSTor Egge #endif
429f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
430f9e354dfSJulian Elischer 		/*
431f9e354dfSJulian Elischer 		 * If the addr to forward to is one of ours, we pretend to
432f9e354dfSJulian Elischer 		 * be the destination for this packet.
433f9e354dfSJulian Elischer 		 */
434f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr != NULL &&
435f9e354dfSJulian Elischer 			IA_SIN(ia)->sin_addr.s_addr ==
436f9e354dfSJulian Elischer 					 ip_fw_fwd_addr->sin_addr.s_addr)
437f9e354dfSJulian Elischer 			goto ours;
438f9e354dfSJulian Elischer #endif
4396ed666afSPoul-Henning Kamp 		if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
440df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
441df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr)
442df8bae1dSRodney W. Grimes 				goto ours;
443df8bae1dSRodney W. Grimes 			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
444df8bae1dSRodney W. Grimes 				goto ours;
445df8bae1dSRodney W. Grimes 		}
446df8bae1dSRodney W. Grimes 	}
447df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
448df8bae1dSRodney W. Grimes 		struct in_multi *inm;
449df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
450df8bae1dSRodney W. Grimes 			/*
451df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
452df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
453df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
454df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
455df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
456df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
457df8bae1dSRodney W. Grimes 			 *
458df8bae1dSRodney W. Grimes 			 * (The IP ident field is put in the same byte order
459df8bae1dSRodney W. Grimes 			 * as expected when ip_mforward() is called from
460df8bae1dSRodney W. Grimes 			 * ip_output().)
461df8bae1dSRodney W. Grimes 			 */
462df8bae1dSRodney W. Grimes 			ip->ip_id = htons(ip->ip_id);
463f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
464df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
465df8bae1dSRodney W. Grimes 				m_freem(m);
466c67b1d17SGarrett Wollman 				return;
467df8bae1dSRodney W. Grimes 			}
468df8bae1dSRodney W. Grimes 			ip->ip_id = ntohs(ip->ip_id);
469df8bae1dSRodney W. Grimes 
470df8bae1dSRodney W. Grimes 			/*
471df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
472df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
473df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
474df8bae1dSRodney W. Grimes 			 */
475df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
476df8bae1dSRodney W. Grimes 				goto ours;
477df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
478df8bae1dSRodney W. Grimes 		}
479df8bae1dSRodney W. Grimes 		/*
480df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
481df8bae1dSRodney W. Grimes 		 * arrival interface.
482df8bae1dSRodney W. Grimes 		 */
483df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
484df8bae1dSRodney W. Grimes 		if (inm == NULL) {
48582c39223SGarrett Wollman 			ipstat.ips_notmember++;
486df8bae1dSRodney W. Grimes 			m_freem(m);
487c67b1d17SGarrett Wollman 			return;
488df8bae1dSRodney W. Grimes 		}
489df8bae1dSRodney W. Grimes 		goto ours;
490df8bae1dSRodney W. Grimes 	}
491df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
492df8bae1dSRodney W. Grimes 		goto ours;
493df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
494df8bae1dSRodney W. Grimes 		goto ours;
495df8bae1dSRodney W. Grimes 
496df8bae1dSRodney W. Grimes 	/*
497df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
498df8bae1dSRodney W. Grimes 	 */
499df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
500df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
501df8bae1dSRodney W. Grimes 		m_freem(m);
502df8bae1dSRodney W. Grimes 	} else
503df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
504c67b1d17SGarrett Wollman 	return;
505df8bae1dSRodney W. Grimes 
506df8bae1dSRodney W. Grimes ours:
507100ba1a6SJordan K. Hubbard 
50863f8d699SJordan K. Hubbard 	/*
509df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
510df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
511df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
512df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
513df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
514df8bae1dSRodney W. Grimes 	 */
515c383a33fSDima Ruban 	if (ip->ip_off & (IP_MF | IP_OFFMASK | IP_RF)) {
516df8bae1dSRodney W. Grimes 		if (m->m_flags & M_EXT) {		/* XXX */
517df8bae1dSRodney W. Grimes 			if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
518df8bae1dSRodney W. Grimes 				ipstat.ips_toosmall++;
51993e0e116SJulian Elischer #ifdef IPDIVERT
52093e0e116SJulian Elischer 				frag_divert_port = 0;
521c977d4c7SJulian Elischer 				ip_divert_cookie = 0;
52293e0e116SJulian Elischer #endif
523c67b1d17SGarrett Wollman 				return;
524df8bae1dSRodney W. Grimes 			}
525df8bae1dSRodney W. Grimes 			ip = mtod(m, struct ip *);
526df8bae1dSRodney W. Grimes 		}
527194a213eSAndrey A. Chernov 		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
528df8bae1dSRodney W. Grimes 		/*
529df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
530df8bae1dSRodney W. Grimes 		 * of this datagram.
531df8bae1dSRodney W. Grimes 		 */
532194a213eSAndrey A. Chernov 		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
533df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
534df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
535df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
536df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
537df8bae1dSRodney W. Grimes 				goto found;
538df8bae1dSRodney W. Grimes 
539194a213eSAndrey A. Chernov 		fp = 0;
540194a213eSAndrey A. Chernov 
541194a213eSAndrey A. Chernov 		/* check if there's a place for the new queue */
542194a213eSAndrey A. Chernov 		if (nipq > maxnipq) {
543194a213eSAndrey A. Chernov 		    /*
544194a213eSAndrey A. Chernov 		     * drop something from the tail of the current queue
545194a213eSAndrey A. Chernov 		     * before proceeding further
546194a213eSAndrey A. Chernov 		     */
547194a213eSAndrey A. Chernov 		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */
548194a213eSAndrey A. Chernov 			for (i = 0; i < IPREASS_NHASH; i++) {
549194a213eSAndrey A. Chernov 			    if (ipq[i].prev != &ipq[i]) {
550194a213eSAndrey A. Chernov 				ip_freef(ipq[i].prev);
551194a213eSAndrey A. Chernov 				break;
552194a213eSAndrey A. Chernov 			    }
553194a213eSAndrey A. Chernov 			}
554194a213eSAndrey A. Chernov 		    } else
555194a213eSAndrey A. Chernov 			ip_freef(ipq[sum].prev);
556194a213eSAndrey A. Chernov 		}
557194a213eSAndrey A. Chernov found:
558df8bae1dSRodney W. Grimes 		/*
559df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
560df8bae1dSRodney W. Grimes 		 * set ip_mff if more fragments are expected,
561df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
562df8bae1dSRodney W. Grimes 		 */
563df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
564df8bae1dSRodney W. Grimes 		((struct ipasfrag *)ip)->ipf_mff &= ~1;
565df8bae1dSRodney W. Grimes 		if (ip->ip_off & IP_MF)
566df8bae1dSRodney W. Grimes 			((struct ipasfrag *)ip)->ipf_mff |= 1;
567df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
568df8bae1dSRodney W. Grimes 
569df8bae1dSRodney W. Grimes 		/*
570df8bae1dSRodney W. Grimes 		 * If datagram marked as having more fragments
571df8bae1dSRodney W. Grimes 		 * or if this is not the first fragment,
572df8bae1dSRodney W. Grimes 		 * attempt reassembly; if it succeeds, proceed.
573df8bae1dSRodney W. Grimes 		 */
574df8bae1dSRodney W. Grimes 		if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
575df8bae1dSRodney W. Grimes 			ipstat.ips_fragments++;
576194a213eSAndrey A. Chernov 			ip = ip_reass((struct ipasfrag *)ip, fp, &ipq[sum]);
577f9e354dfSJulian Elischer 			if (ip == 0) {
578f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
579f9e354dfSJulian Elischer 				ip_fw_fwd_addr = NULL;
580f9e354dfSJulian Elischer #endif
581c67b1d17SGarrett Wollman 				return;
582f9e354dfSJulian Elischer 			}
58381aee63dSPoul-Henning Kamp 			/* Get the length of the reassembled packets header */
58481aee63dSPoul-Henning Kamp 			hlen = IP_VHL_HL(ip->ip_vhl) << 2;
585df8bae1dSRodney W. Grimes 			ipstat.ips_reassembled++;
586df8bae1dSRodney W. Grimes 			m = dtom(ip);
587af782f1cSBrian Somers #ifdef IPDIVERT
588af782f1cSBrian Somers 			if (frag_divert_port) {
589af782f1cSBrian Somers 				ip->ip_len += hlen;
590af782f1cSBrian Somers 				HTONS(ip->ip_len);
591af782f1cSBrian Somers 				HTONS(ip->ip_off);
592af782f1cSBrian Somers 				HTONS(ip->ip_id);
593af782f1cSBrian Somers 				ip->ip_sum = 0;
594af782f1cSBrian Somers 				ip->ip_sum = in_cksum_hdr(ip);
595af782f1cSBrian Somers 				NTOHS(ip->ip_id);
596af782f1cSBrian Somers 				NTOHS(ip->ip_off);
597af782f1cSBrian Somers 				NTOHS(ip->ip_len);
598af782f1cSBrian Somers 				ip->ip_len -= hlen;
599af782f1cSBrian Somers 			}
600af782f1cSBrian Somers #endif
601df8bae1dSRodney W. Grimes 		} else
602df8bae1dSRodney W. Grimes 			if (fp)
603df8bae1dSRodney W. Grimes 				ip_freef(fp);
604df8bae1dSRodney W. Grimes 	} else
605df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
606df8bae1dSRodney W. Grimes 
60793e0e116SJulian Elischer #ifdef IPDIVERT
60893e0e116SJulian Elischer 	/*
609e4676ba6SJulian Elischer 	 * Divert reassembled packets to the divert protocol if required
61093e0e116SJulian Elischer 	 */
61193e0e116SJulian Elischer 	if (frag_divert_port) {
612e4676ba6SJulian Elischer 		ipstat.ips_delivered++;
61393e0e116SJulian Elischer 		ip_divert_port = frag_divert_port;
61493e0e116SJulian Elischer 		frag_divert_port = 0;
61593e0e116SJulian Elischer 		(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);
61693e0e116SJulian Elischer 		return;
61793e0e116SJulian Elischer 	}
61879755dc5SJulian Elischer 
61979755dc5SJulian Elischer 	/* Don't let packets divert themselves */
62079755dc5SJulian Elischer 	if (ip->ip_p == IPPROTO_DIVERT) {
62179755dc5SJulian Elischer 		ipstat.ips_noproto++;
62279755dc5SJulian Elischer 		goto bad;
62379755dc5SJulian Elischer 	}
624bb60f459SJulian Elischer 
62593e0e116SJulian Elischer #endif
62693e0e116SJulian Elischer 
627df8bae1dSRodney W. Grimes 	/*
628df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
629df8bae1dSRodney W. Grimes 	 */
630df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
631df8bae1dSRodney W. Grimes 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
632f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
633f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;	/* tcp needed it */
634f9e354dfSJulian Elischer #endif
635c67b1d17SGarrett Wollman 	return;
636df8bae1dSRodney W. Grimes bad:
637f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
638f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;
639f9e354dfSJulian Elischer #endif
640df8bae1dSRodney W. Grimes 	m_freem(m);
641c67b1d17SGarrett Wollman }
642c67b1d17SGarrett Wollman 
643c67b1d17SGarrett Wollman /*
644c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
645c67b1d17SGarrett Wollman  */
646c67b1d17SGarrett Wollman static void
647c67b1d17SGarrett Wollman ipintr(void)
648c67b1d17SGarrett Wollman {
649c67b1d17SGarrett Wollman 	int s;
650c67b1d17SGarrett Wollman 	struct mbuf *m;
651c67b1d17SGarrett Wollman 
652c67b1d17SGarrett Wollman 	while(1) {
653c67b1d17SGarrett Wollman 		s = splimp();
654c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
655c67b1d17SGarrett Wollman 		splx(s);
656c67b1d17SGarrett Wollman 		if (m == 0)
657c67b1d17SGarrett Wollman 			return;
658c67b1d17SGarrett Wollman 		ip_input(m);
659c67b1d17SGarrett Wollman 	}
660df8bae1dSRodney W. Grimes }
661df8bae1dSRodney W. Grimes 
662748e0b0aSGarrett Wollman NETISR_SET(NETISR_IP, ipintr);
663748e0b0aSGarrett Wollman 
664df8bae1dSRodney W. Grimes /*
665df8bae1dSRodney W. Grimes  * Take incoming datagram fragment and try to
666df8bae1dSRodney W. Grimes  * reassemble it into whole datagram.  If a chain for
667df8bae1dSRodney W. Grimes  * reassembly of this datagram already exists, then it
668df8bae1dSRodney W. Grimes  * is given as fp; otherwise have to make a chain.
669df8bae1dSRodney W. Grimes  */
6700312fbe9SPoul-Henning Kamp static struct ip *
671194a213eSAndrey A. Chernov ip_reass(ip, fp, where)
672df8bae1dSRodney W. Grimes 	register struct ipasfrag *ip;
673df8bae1dSRodney W. Grimes 	register struct ipq *fp;
674194a213eSAndrey A. Chernov 	struct   ipq    *where;
675df8bae1dSRodney W. Grimes {
676df8bae1dSRodney W. Grimes 	register struct mbuf *m = dtom(ip);
677df8bae1dSRodney W. Grimes 	register struct ipasfrag *q;
678df8bae1dSRodney W. Grimes 	struct mbuf *t;
679df8bae1dSRodney W. Grimes 	int hlen = ip->ip_hl << 2;
680df8bae1dSRodney W. Grimes 	int i, next;
681df8bae1dSRodney W. Grimes 
682df8bae1dSRodney W. Grimes 	/*
683df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
684df8bae1dSRodney W. Grimes 	 * would confuse code below.
685df8bae1dSRodney W. Grimes 	 */
686df8bae1dSRodney W. Grimes 	m->m_data += hlen;
687df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
688df8bae1dSRodney W. Grimes 
689df8bae1dSRodney W. Grimes 	/*
690df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
691df8bae1dSRodney W. Grimes 	 */
692df8bae1dSRodney W. Grimes 	if (fp == 0) {
693df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
694df8bae1dSRodney W. Grimes 			goto dropfrag;
695df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
696194a213eSAndrey A. Chernov 		insque(fp, where);
697194a213eSAndrey A. Chernov 		nipq++;
698df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
699df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
700df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
701df8bae1dSRodney W. Grimes 		fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
702df8bae1dSRodney W. Grimes 		fp->ipq_src = ((struct ip *)ip)->ip_src;
703df8bae1dSRodney W. Grimes 		fp->ipq_dst = ((struct ip *)ip)->ip_dst;
70493e0e116SJulian Elischer #ifdef IPDIVERT
70593e0e116SJulian Elischer 		fp->ipq_divert = 0;
706bb60f459SJulian Elischer 		fp->ipq_div_cookie = 0;
70793e0e116SJulian Elischer #endif
708df8bae1dSRodney W. Grimes 		q = (struct ipasfrag *)fp;
709df8bae1dSRodney W. Grimes 		goto insert;
710df8bae1dSRodney W. Grimes 	}
711df8bae1dSRodney W. Grimes 
712df8bae1dSRodney W. Grimes 	/*
713df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
714df8bae1dSRodney W. Grimes 	 */
715df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
716df8bae1dSRodney W. Grimes 		if (q->ip_off > ip->ip_off)
717df8bae1dSRodney W. Grimes 			break;
718df8bae1dSRodney W. Grimes 
719df8bae1dSRodney W. Grimes 	/*
720df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
721df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
722df8bae1dSRodney W. Grimes 	 * segment.  If it provides all of our data, drop us.
723df8bae1dSRodney W. Grimes 	 */
724df8bae1dSRodney W. Grimes 	if (q->ipf_prev != (struct ipasfrag *)fp) {
725df8bae1dSRodney W. Grimes 		i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
726df8bae1dSRodney W. Grimes 		if (i > 0) {
727df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
728df8bae1dSRodney W. Grimes 				goto dropfrag;
729df8bae1dSRodney W. Grimes 			m_adj(dtom(ip), i);
730df8bae1dSRodney W. Grimes 			ip->ip_off += i;
731df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
732df8bae1dSRodney W. Grimes 		}
733df8bae1dSRodney W. Grimes 	}
734df8bae1dSRodney W. Grimes 
735df8bae1dSRodney W. Grimes 	/*
736df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
737df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
738df8bae1dSRodney W. Grimes 	 */
739df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
740e7c81944SDavid Greenman 		struct mbuf *m0;
741e7c81944SDavid Greenman 
742df8bae1dSRodney W. Grimes 		i = (ip->ip_off + ip->ip_len) - q->ip_off;
743df8bae1dSRodney W. Grimes 		if (i < q->ip_len) {
744df8bae1dSRodney W. Grimes 			q->ip_len -= i;
745df8bae1dSRodney W. Grimes 			q->ip_off += i;
746df8bae1dSRodney W. Grimes 			m_adj(dtom(q), i);
747df8bae1dSRodney W. Grimes 			break;
748df8bae1dSRodney W. Grimes 		}
749e7c81944SDavid Greenman 		m0 = dtom(q);
750df8bae1dSRodney W. Grimes 		q = q->ipf_next;
751df8bae1dSRodney W. Grimes 		ip_deq(q->ipf_prev);
752e7c81944SDavid Greenman 		m_freem(m0);
753df8bae1dSRodney W. Grimes 	}
754df8bae1dSRodney W. Grimes 
755df8bae1dSRodney W. Grimes insert:
75693e0e116SJulian Elischer 
75793e0e116SJulian Elischer #ifdef IPDIVERT
75893e0e116SJulian Elischer 	/*
75993e0e116SJulian Elischer 	 * Any fragment diverting causes the whole packet to divert
76093e0e116SJulian Elischer 	 */
761bb60f459SJulian Elischer 	if (frag_divert_port != 0) {
76293e0e116SJulian Elischer 		fp->ipq_divert = frag_divert_port;
763c977d4c7SJulian Elischer 		fp->ipq_div_cookie = ip_divert_cookie;
764bb60f459SJulian Elischer 	}
76593e0e116SJulian Elischer 	frag_divert_port = 0;
766c977d4c7SJulian Elischer 	ip_divert_cookie = 0;
76793e0e116SJulian Elischer #endif
76893e0e116SJulian Elischer 
769df8bae1dSRodney W. Grimes 	/*
770df8bae1dSRodney W. Grimes 	 * Stick new segment in its place;
771df8bae1dSRodney W. Grimes 	 * check for complete reassembly.
772df8bae1dSRodney W. Grimes 	 */
773df8bae1dSRodney W. Grimes 	ip_enq(ip, q->ipf_prev);
774df8bae1dSRodney W. Grimes 	next = 0;
775df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
776df8bae1dSRodney W. Grimes 		if (q->ip_off != next)
777df8bae1dSRodney W. Grimes 			return (0);
778df8bae1dSRodney W. Grimes 		next += q->ip_len;
779df8bae1dSRodney W. Grimes 	}
780df8bae1dSRodney W. Grimes 	if (q->ipf_prev->ipf_mff & 1)
781df8bae1dSRodney W. Grimes 		return (0);
782df8bae1dSRodney W. Grimes 
783df8bae1dSRodney W. Grimes 	/*
784430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
785430d30d8SBill Fenner 	 */
786430d30d8SBill Fenner 	if (next + (IP_VHL_HL(((struct ip *)fp->ipq_next)->ip_vhl) << 2)
787430d30d8SBill Fenner 							> IP_MAXPACKET) {
788430d30d8SBill Fenner 		ipstat.ips_toolong++;
789430d30d8SBill Fenner 		ip_freef(fp);
790430d30d8SBill Fenner 		return (0);
791430d30d8SBill Fenner 	}
792430d30d8SBill Fenner 
793430d30d8SBill Fenner 	/*
794430d30d8SBill Fenner 	 * Concatenate fragments.
795df8bae1dSRodney W. Grimes 	 */
796df8bae1dSRodney W. Grimes 	q = fp->ipq_next;
797df8bae1dSRodney W. Grimes 	m = dtom(q);
798df8bae1dSRodney W. Grimes 	t = m->m_next;
799df8bae1dSRodney W. Grimes 	m->m_next = 0;
800df8bae1dSRodney W. Grimes 	m_cat(m, t);
801df8bae1dSRodney W. Grimes 	q = q->ipf_next;
802df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp) {
803df8bae1dSRodney W. Grimes 		t = dtom(q);
804df8bae1dSRodney W. Grimes 		q = q->ipf_next;
805df8bae1dSRodney W. Grimes 		m_cat(m, t);
806df8bae1dSRodney W. Grimes 	}
807df8bae1dSRodney W. Grimes 
80893e0e116SJulian Elischer #ifdef IPDIVERT
80993e0e116SJulian Elischer 	/*
810c977d4c7SJulian Elischer 	 * extract divert port for packet, if any
81193e0e116SJulian Elischer 	 */
81293e0e116SJulian Elischer 	frag_divert_port = fp->ipq_divert;
813c977d4c7SJulian Elischer 	ip_divert_cookie = fp->ipq_div_cookie;
81493e0e116SJulian Elischer #endif
81593e0e116SJulian Elischer 
816df8bae1dSRodney W. Grimes 	/*
817df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
818df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
819df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
820df8bae1dSRodney W. Grimes 	 * Make header visible.
821df8bae1dSRodney W. Grimes 	 */
822df8bae1dSRodney W. Grimes 	ip = fp->ipq_next;
823df8bae1dSRodney W. Grimes 	ip->ip_len = next;
824df8bae1dSRodney W. Grimes 	ip->ipf_mff &= ~1;
825df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_src = fp->ipq_src;
826df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_dst = fp->ipq_dst;
827df8bae1dSRodney W. Grimes 	remque(fp);
828194a213eSAndrey A. Chernov 	nipq--;
829df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
830df8bae1dSRodney W. Grimes 	m = dtom(ip);
831df8bae1dSRodney W. Grimes 	m->m_len += (ip->ip_hl << 2);
832df8bae1dSRodney W. Grimes 	m->m_data -= (ip->ip_hl << 2);
833df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
834df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
835df8bae1dSRodney W. Grimes 		register int plen = 0;
836df8bae1dSRodney W. Grimes 		for (t = m; m; m = m->m_next)
837df8bae1dSRodney W. Grimes 			plen += m->m_len;
838df8bae1dSRodney W. Grimes 		t->m_pkthdr.len = plen;
839df8bae1dSRodney W. Grimes 	}
840df8bae1dSRodney W. Grimes 	return ((struct ip *)ip);
841df8bae1dSRodney W. Grimes 
842df8bae1dSRodney W. Grimes dropfrag:
843df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
844df8bae1dSRodney W. Grimes 	m_freem(m);
845df8bae1dSRodney W. Grimes 	return (0);
846df8bae1dSRodney W. Grimes }
847df8bae1dSRodney W. Grimes 
848df8bae1dSRodney W. Grimes /*
849df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
850df8bae1dSRodney W. Grimes  * associated datagrams.
851df8bae1dSRodney W. Grimes  */
8520312fbe9SPoul-Henning Kamp static void
853df8bae1dSRodney W. Grimes ip_freef(fp)
854df8bae1dSRodney W. Grimes 	struct ipq *fp;
855df8bae1dSRodney W. Grimes {
856df8bae1dSRodney W. Grimes 	register struct ipasfrag *q, *p;
857df8bae1dSRodney W. Grimes 
858df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
859df8bae1dSRodney W. Grimes 		p = q->ipf_next;
860df8bae1dSRodney W. Grimes 		ip_deq(q);
861df8bae1dSRodney W. Grimes 		m_freem(dtom(q));
862df8bae1dSRodney W. Grimes 	}
863df8bae1dSRodney W. Grimes 	remque(fp);
864df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
865194a213eSAndrey A. Chernov 	nipq--;
866df8bae1dSRodney W. Grimes }
867df8bae1dSRodney W. Grimes 
868df8bae1dSRodney W. Grimes /*
869df8bae1dSRodney W. Grimes  * Put an ip fragment on a reassembly chain.
870df8bae1dSRodney W. Grimes  * Like insque, but pointers in middle of structure.
871df8bae1dSRodney W. Grimes  */
8720312fbe9SPoul-Henning Kamp static void
873df8bae1dSRodney W. Grimes ip_enq(p, prev)
874df8bae1dSRodney W. Grimes 	register struct ipasfrag *p, *prev;
875df8bae1dSRodney W. Grimes {
876df8bae1dSRodney W. Grimes 
877df8bae1dSRodney W. Grimes 	p->ipf_prev = prev;
878df8bae1dSRodney W. Grimes 	p->ipf_next = prev->ipf_next;
879df8bae1dSRodney W. Grimes 	prev->ipf_next->ipf_prev = p;
880df8bae1dSRodney W. Grimes 	prev->ipf_next = p;
881df8bae1dSRodney W. Grimes }
882df8bae1dSRodney W. Grimes 
883df8bae1dSRodney W. Grimes /*
884df8bae1dSRodney W. Grimes  * To ip_enq as remque is to insque.
885df8bae1dSRodney W. Grimes  */
8860312fbe9SPoul-Henning Kamp static void
887df8bae1dSRodney W. Grimes ip_deq(p)
888df8bae1dSRodney W. Grimes 	register struct ipasfrag *p;
889df8bae1dSRodney W. Grimes {
890df8bae1dSRodney W. Grimes 
891df8bae1dSRodney W. Grimes 	p->ipf_prev->ipf_next = p->ipf_next;
892df8bae1dSRodney W. Grimes 	p->ipf_next->ipf_prev = p->ipf_prev;
893df8bae1dSRodney W. Grimes }
894df8bae1dSRodney W. Grimes 
895df8bae1dSRodney W. Grimes /*
896df8bae1dSRodney W. Grimes  * IP timer processing;
897df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
898df8bae1dSRodney W. Grimes  * queue, discard it.
899df8bae1dSRodney W. Grimes  */
900df8bae1dSRodney W. Grimes void
901df8bae1dSRodney W. Grimes ip_slowtimo()
902df8bae1dSRodney W. Grimes {
903df8bae1dSRodney W. Grimes 	register struct ipq *fp;
904df8bae1dSRodney W. Grimes 	int s = splnet();
905194a213eSAndrey A. Chernov 	int i;
906df8bae1dSRodney W. Grimes 
907194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
908194a213eSAndrey A. Chernov 		fp = ipq[i].next;
909194a213eSAndrey A. Chernov 		if (fp == 0)
910194a213eSAndrey A. Chernov 			continue;
911194a213eSAndrey A. Chernov 		while (fp != &ipq[i]) {
912df8bae1dSRodney W. Grimes 			--fp->ipq_ttl;
913df8bae1dSRodney W. Grimes 			fp = fp->next;
914df8bae1dSRodney W. Grimes 			if (fp->prev->ipq_ttl == 0) {
915df8bae1dSRodney W. Grimes 				ipstat.ips_fragtimeout++;
916df8bae1dSRodney W. Grimes 				ip_freef(fp->prev);
917df8bae1dSRodney W. Grimes 			}
918df8bae1dSRodney W. Grimes 		}
919194a213eSAndrey A. Chernov 	}
9201f91d8c5SDavid Greenman 	ipflow_slowtimo();
921df8bae1dSRodney W. Grimes 	splx(s);
922df8bae1dSRodney W. Grimes }
923df8bae1dSRodney W. Grimes 
924df8bae1dSRodney W. Grimes /*
925df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
926df8bae1dSRodney W. Grimes  */
927df8bae1dSRodney W. Grimes void
928df8bae1dSRodney W. Grimes ip_drain()
929df8bae1dSRodney W. Grimes {
930194a213eSAndrey A. Chernov 	int     i;
931ce29ab3aSGarrett Wollman 
932194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
933194a213eSAndrey A. Chernov 		while (ipq[i].next != &ipq[i]) {
934194a213eSAndrey A. Chernov 			ipstat.ips_fragdropped++;
935194a213eSAndrey A. Chernov 			ip_freef(ipq[i].next);
936194a213eSAndrey A. Chernov 		}
937194a213eSAndrey A. Chernov 	}
938ce29ab3aSGarrett Wollman 	in_rtqdrain();
939df8bae1dSRodney W. Grimes }
940df8bae1dSRodney W. Grimes 
941df8bae1dSRodney W. Grimes /*
942df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
943df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
944df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
945df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
946df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
947df8bae1dSRodney W. Grimes  */
9480312fbe9SPoul-Henning Kamp static int
949df8bae1dSRodney W. Grimes ip_dooptions(m)
950df8bae1dSRodney W. Grimes 	struct mbuf *m;
951df8bae1dSRodney W. Grimes {
952df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
953df8bae1dSRodney W. Grimes 	register u_char *cp;
954df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
955df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
956df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
957df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
958df8bae1dSRodney W. Grimes 	n_time ntime;
959df8bae1dSRodney W. Grimes 
960df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
961df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
96258938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
963df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
964df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
965df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
966df8bae1dSRodney W. Grimes 			break;
967df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
968df8bae1dSRodney W. Grimes 			optlen = 1;
969df8bae1dSRodney W. Grimes 		else {
970df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
971df8bae1dSRodney W. Grimes 			if (optlen <= 0 || optlen > cnt) {
972df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
973df8bae1dSRodney W. Grimes 				goto bad;
974df8bae1dSRodney W. Grimes 			}
975df8bae1dSRodney W. Grimes 		}
976df8bae1dSRodney W. Grimes 		switch (opt) {
977df8bae1dSRodney W. Grimes 
978df8bae1dSRodney W. Grimes 		default:
979df8bae1dSRodney W. Grimes 			break;
980df8bae1dSRodney W. Grimes 
981df8bae1dSRodney W. Grimes 		/*
982df8bae1dSRodney W. Grimes 		 * Source routing with record.
983df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
984df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
985df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
986df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
987df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
988df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
989df8bae1dSRodney W. Grimes 		 */
990df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
991df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
992df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
993df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
994df8bae1dSRodney W. Grimes 				goto bad;
995df8bae1dSRodney W. Grimes 			}
996df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
997df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
998df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
999df8bae1dSRodney W. Grimes 			if (ia == 0) {
1000df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
1001df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
1002df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
1003df8bae1dSRodney W. Grimes 					goto bad;
1004df8bae1dSRodney W. Grimes 				}
1005bc189bf8SGuido van Rooij 				if (!ip_dosourceroute)
1006bc189bf8SGuido van Rooij 					goto nosourcerouting;
1007df8bae1dSRodney W. Grimes 				/*
1008df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
1009df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
1010df8bae1dSRodney W. Grimes 				 */
1011df8bae1dSRodney W. Grimes 				break;
1012df8bae1dSRodney W. Grimes 			}
1013df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1014df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr)) {
1015df8bae1dSRodney W. Grimes 				/*
1016df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
1017df8bae1dSRodney W. Grimes 				 */
10184fce5804SGuido van Rooij 				if (!ip_acceptsourceroute)
10194fce5804SGuido van Rooij 					goto nosourcerouting;
1020df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
1021df8bae1dSRodney W. Grimes 				break;
1022df8bae1dSRodney W. Grimes 			}
10231025071fSGarrett Wollman 
10241025071fSGarrett Wollman 			if (!ip_dosourceroute) {
10251025071fSGarrett Wollman 				char buf[4*sizeof "123"];
10261025071fSGarrett Wollman 
1027efa48587SGuido van Rooij nosourcerouting:
1028bc189bf8SGuido van Rooij 				strcpy(buf, inet_ntoa(ip->ip_dst));
10291025071fSGarrett Wollman 				log(LOG_WARNING,
10301025071fSGarrett Wollman 				    "attempted source route from %s to %s\n",
10311025071fSGarrett Wollman 				    inet_ntoa(ip->ip_src), buf);
10321025071fSGarrett Wollman 				type = ICMP_UNREACH;
10331025071fSGarrett Wollman 				code = ICMP_UNREACH_SRCFAIL;
10341025071fSGarrett Wollman 				goto bad;
10351025071fSGarrett Wollman 			}
10361025071fSGarrett Wollman 
1037df8bae1dSRodney W. Grimes 			/*
1038df8bae1dSRodney W. Grimes 			 * locate outgoing interface
1039df8bae1dSRodney W. Grimes 			 */
104094a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
1041df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
10421025071fSGarrett Wollman 
1043df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
1044df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
1045df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
1046df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
1047df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
1048df8bae1dSRodney W. Grimes 			} else
1049df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
1050df8bae1dSRodney W. Grimes 			if (ia == 0) {
1051df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1052df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
1053df8bae1dSRodney W. Grimes 				goto bad;
1054df8bae1dSRodney W. Grimes 			}
1055df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
105694a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
105794a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1058df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1059df8bae1dSRodney W. Grimes 			/*
1060df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
1061df8bae1dSRodney W. Grimes 			 */
1062df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1063df8bae1dSRodney W. Grimes 			break;
1064df8bae1dSRodney W. Grimes 
1065df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1066df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1067df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1068df8bae1dSRodney W. Grimes 				goto bad;
1069df8bae1dSRodney W. Grimes 			}
1070df8bae1dSRodney W. Grimes 			/*
1071df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1072df8bae1dSRodney W. Grimes 			 */
1073df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1074df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr))
1075df8bae1dSRodney W. Grimes 				break;
107694a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1077df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1078df8bae1dSRodney W. Grimes 			/*
1079df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1080df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1081df8bae1dSRodney W. Grimes 			 */
1082df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
1083df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
1084df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1085df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1086df8bae1dSRodney W. Grimes 				goto bad;
1087df8bae1dSRodney W. Grimes 			}
108894a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
108994a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1090df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1091df8bae1dSRodney W. Grimes 			break;
1092df8bae1dSRodney W. Grimes 
1093df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1094df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
1095df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
1096df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
1097df8bae1dSRodney W. Grimes 				goto bad;
1098df8bae1dSRodney W. Grimes 			if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
1099df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
1100df8bae1dSRodney W. Grimes 					goto bad;
1101df8bae1dSRodney W. Grimes 				break;
1102df8bae1dSRodney W. Grimes 			}
1103df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
1104df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
1105df8bae1dSRodney W. Grimes 
1106df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1107df8bae1dSRodney W. Grimes 				break;
1108df8bae1dSRodney W. Grimes 
1109df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
1110b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1111df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1112df8bae1dSRodney W. Grimes 					goto bad;
1113df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1114df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1115df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
1116df8bae1dSRodney W. Grimes 				if (ia == 0)
1117df8bae1dSRodney W. Grimes 					continue;
111894a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
111994a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
1120df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1121df8bae1dSRodney W. Grimes 				break;
1122df8bae1dSRodney W. Grimes 
1123df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1124b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1125df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1126df8bae1dSRodney W. Grimes 					goto bad;
112794a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1128df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1129df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1130df8bae1dSRodney W. Grimes 					continue;
1131df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1132df8bae1dSRodney W. Grimes 				break;
1133df8bae1dSRodney W. Grimes 
1134df8bae1dSRodney W. Grimes 			default:
1135df8bae1dSRodney W. Grimes 				goto bad;
1136df8bae1dSRodney W. Grimes 			}
1137df8bae1dSRodney W. Grimes 			ntime = iptime();
113894a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1139df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1140df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1141df8bae1dSRodney W. Grimes 		}
1142df8bae1dSRodney W. Grimes 	}
114347174b49SAndrey A. Chernov 	if (forward && ipforwarding) {
1144df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1145df8bae1dSRodney W. Grimes 		return (1);
1146df8bae1dSRodney W. Grimes 	}
1147df8bae1dSRodney W. Grimes 	return (0);
1148df8bae1dSRodney W. Grimes bad:
114958938916SGarrett Wollman 	ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2;   /* XXX icmp_error adds in hdr length */
1150df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1151df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1152df8bae1dSRodney W. Grimes 	return (1);
1153df8bae1dSRodney W. Grimes }
1154df8bae1dSRodney W. Grimes 
1155df8bae1dSRodney W. Grimes /*
1156df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1157df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1158df8bae1dSRodney W. Grimes  */
11590312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1160df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1161df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1162df8bae1dSRodney W. Grimes {
1163df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1164df8bae1dSRodney W. Grimes 
1165df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1166df8bae1dSRodney W. Grimes 
1167df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1168df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1169df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1170df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1171df8bae1dSRodney W. Grimes 		}
1172df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1173df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1174df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1175df8bae1dSRodney W. Grimes 
11762c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1177df8bae1dSRodney W. Grimes 	}
1178df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1179df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1180df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1181df8bae1dSRodney W. Grimes }
1182df8bae1dSRodney W. Grimes 
1183df8bae1dSRodney W. Grimes /*
1184df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1185df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1186df8bae1dSRodney W. Grimes  */
1187df8bae1dSRodney W. Grimes void
1188df8bae1dSRodney W. Grimes save_rte(option, dst)
1189df8bae1dSRodney W. Grimes 	u_char *option;
1190df8bae1dSRodney W. Grimes 	struct in_addr dst;
1191df8bae1dSRodney W. Grimes {
1192df8bae1dSRodney W. Grimes 	unsigned olen;
1193df8bae1dSRodney W. Grimes 
1194df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1195df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1196df8bae1dSRodney W. Grimes 	if (ipprintfs)
1197df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1198df8bae1dSRodney W. Grimes #endif
1199df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1200df8bae1dSRodney W. Grimes 		return;
12010453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1202df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1203df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1204df8bae1dSRodney W. Grimes }
1205df8bae1dSRodney W. Grimes 
1206df8bae1dSRodney W. Grimes /*
1207df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1208df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1209df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1210df8bae1dSRodney W. Grimes  */
1211df8bae1dSRodney W. Grimes struct mbuf *
1212df8bae1dSRodney W. Grimes ip_srcroute()
1213df8bae1dSRodney W. Grimes {
1214df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1215df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1216df8bae1dSRodney W. Grimes 
1217df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1218df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1219df8bae1dSRodney W. Grimes 	m = m_get(M_DONTWAIT, MT_SOOPTS);
1220df8bae1dSRodney W. Grimes 	if (m == 0)
1221df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1222df8bae1dSRodney W. Grimes 
1223df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1224df8bae1dSRodney W. Grimes 
1225df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1226df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1227df8bae1dSRodney W. Grimes 	    OPTSIZ;
1228df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1229df8bae1dSRodney W. Grimes 	if (ipprintfs)
1230df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1231df8bae1dSRodney W. Grimes #endif
1232df8bae1dSRodney W. Grimes 
1233df8bae1dSRodney W. Grimes 	/*
1234df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1235df8bae1dSRodney W. Grimes 	 */
1236df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1237df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1238df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1239df8bae1dSRodney W. Grimes 	if (ipprintfs)
1240df8bae1dSRodney W. Grimes 		printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
1241df8bae1dSRodney W. Grimes #endif
1242df8bae1dSRodney W. Grimes 
1243df8bae1dSRodney W. Grimes 	/*
1244df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1245df8bae1dSRodney W. Grimes 	 */
1246df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1247df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
124894a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
124994a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1250df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1251df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1252df8bae1dSRodney W. Grimes #undef OPTSIZ
1253df8bae1dSRodney W. Grimes 	/*
1254df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1255df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1256df8bae1dSRodney W. Grimes 	 */
1257df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1258df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1259df8bae1dSRodney W. Grimes 		if (ipprintfs)
1260df8bae1dSRodney W. Grimes 			printf(" %lx", ntohl(q->s_addr));
1261df8bae1dSRodney W. Grimes #endif
1262df8bae1dSRodney W. Grimes 		*q++ = *p--;
1263df8bae1dSRodney W. Grimes 	}
1264df8bae1dSRodney W. Grimes 	/*
1265df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1266df8bae1dSRodney W. Grimes 	 */
1267df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1268df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1269df8bae1dSRodney W. Grimes 	if (ipprintfs)
1270df8bae1dSRodney W. Grimes 		printf(" %lx\n", ntohl(q->s_addr));
1271df8bae1dSRodney W. Grimes #endif
1272df8bae1dSRodney W. Grimes 	return (m);
1273df8bae1dSRodney W. Grimes }
1274df8bae1dSRodney W. Grimes 
1275df8bae1dSRodney W. Grimes /*
1276df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1277df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1278df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1279df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1280df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1281df8bae1dSRodney W. Grimes  */
1282df8bae1dSRodney W. Grimes void
1283df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1284df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1285df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1286df8bae1dSRodney W. Grimes {
1287df8bae1dSRodney W. Grimes 	register int i;
1288df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1289df8bae1dSRodney W. Grimes 	register caddr_t opts;
1290df8bae1dSRodney W. Grimes 	int olen;
1291df8bae1dSRodney W. Grimes 
129258938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1293df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1294df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1295df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1296df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1297df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1298df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
129958938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1300df8bae1dSRodney W. Grimes }
1301df8bae1dSRodney W. Grimes 
1302df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1303df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1304df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1305df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1306df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1307df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1308df8bae1dSRodney W. Grimes 	ENOPROTOOPT
1309df8bae1dSRodney W. Grimes };
1310df8bae1dSRodney W. Grimes 
1311df8bae1dSRodney W. Grimes /*
1312df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1313df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1314df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1315df8bae1dSRodney W. Grimes  * of codes and types.
1316df8bae1dSRodney W. Grimes  *
1317df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1318df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1319df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1320df8bae1dSRodney W. Grimes  * protocol deal with that.
1321df8bae1dSRodney W. Grimes  *
1322df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1323df8bae1dSRodney W. Grimes  * via a source route.
1324df8bae1dSRodney W. Grimes  */
13250312fbe9SPoul-Henning Kamp static void
1326df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1327df8bae1dSRodney W. Grimes 	struct mbuf *m;
1328df8bae1dSRodney W. Grimes 	int srcrt;
1329df8bae1dSRodney W. Grimes {
1330df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1331df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1332df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
133326f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1334df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1335df8bae1dSRodney W. Grimes 	n_long dest;
1336df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
1337df8bae1dSRodney W. Grimes 
1338df8bae1dSRodney W. Grimes 	dest = 0;
1339df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1340df8bae1dSRodney W. Grimes 	if (ipprintfs)
134161ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1342623ae52eSPoul-Henning Kamp 			ip->ip_src.s_addr, ip->ip_dst.s_addr, ip->ip_ttl);
1343df8bae1dSRodney W. Grimes #endif
1344100ba1a6SJordan K. Hubbard 
1345100ba1a6SJordan K. Hubbard 
1346df8bae1dSRodney W. Grimes 	if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
1347df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1348df8bae1dSRodney W. Grimes 		m_freem(m);
1349df8bae1dSRodney W. Grimes 		return;
1350df8bae1dSRodney W. Grimes 	}
1351df8bae1dSRodney W. Grimes 	HTONS(ip->ip_id);
1352df8bae1dSRodney W. Grimes 	if (ip->ip_ttl <= IPTTLDEC) {
1353df8bae1dSRodney W. Grimes 		icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
1354df8bae1dSRodney W. Grimes 		return;
1355df8bae1dSRodney W. Grimes 	}
1356df8bae1dSRodney W. Grimes 	ip->ip_ttl -= IPTTLDEC;
1357df8bae1dSRodney W. Grimes 
1358df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1359df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1360df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1361df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1362df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1363df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1364df8bae1dSRodney W. Grimes 		}
1365df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1366df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1367df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1368df8bae1dSRodney W. Grimes 
13692c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1370df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1371df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1372df8bae1dSRodney W. Grimes 			return;
1373df8bae1dSRodney W. Grimes 		}
1374df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1375df8bae1dSRodney W. Grimes 	}
1376df8bae1dSRodney W. Grimes 
1377df8bae1dSRodney W. Grimes 	/*
1378df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1379df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1380df8bae1dSRodney W. Grimes 	 */
1381df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
1382df8bae1dSRodney W. Grimes 
1383df8bae1dSRodney W. Grimes 	/*
1384df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1385df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1386df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1387df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1388df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1389df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1390df8bae1dSRodney W. Grimes 	 */
1391df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1392df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1393df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1394df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1395df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1396df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1397df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1398df8bae1dSRodney W. Grimes 
1399df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1400df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1401df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1402df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1403df8bae1dSRodney W. Grimes 		    else
1404df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1405df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1406df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1407df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1408df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1409df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1410df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1411df8bae1dSRodney W. Grimes #endif
1412df8bae1dSRodney W. Grimes 		}
1413df8bae1dSRodney W. Grimes 	}
1414df8bae1dSRodney W. Grimes 
1415b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1416b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1417df8bae1dSRodney W. Grimes 	if (error)
1418df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1419df8bae1dSRodney W. Grimes 	else {
1420df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1421df8bae1dSRodney W. Grimes 		if (type)
1422df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1423df8bae1dSRodney W. Grimes 		else {
14241f91d8c5SDavid Greenman 			if (mcopy) {
14251f91d8c5SDavid Greenman 				ipflow_create(&ipforward_rt, mcopy);
1426df8bae1dSRodney W. Grimes 				m_freem(mcopy);
14271f91d8c5SDavid Greenman 			}
1428df8bae1dSRodney W. Grimes 			return;
1429df8bae1dSRodney W. Grimes 		}
1430df8bae1dSRodney W. Grimes 	}
1431df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1432df8bae1dSRodney W. Grimes 		return;
1433df8bae1dSRodney W. Grimes 	destifp = NULL;
1434df8bae1dSRodney W. Grimes 
1435df8bae1dSRodney W. Grimes 	switch (error) {
1436df8bae1dSRodney W. Grimes 
1437df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1438df8bae1dSRodney W. Grimes 		/* type, code set above */
1439df8bae1dSRodney W. Grimes 		break;
1440df8bae1dSRodney W. Grimes 
1441df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1442df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1443df8bae1dSRodney W. Grimes 	case ENETDOWN:
1444df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1445df8bae1dSRodney W. Grimes 	default:
1446df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1447df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1448df8bae1dSRodney W. Grimes 		break;
1449df8bae1dSRodney W. Grimes 
1450df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1451df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1452df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
1453df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1454df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
1455df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1456df8bae1dSRodney W. Grimes 		break;
1457df8bae1dSRodney W. Grimes 
1458df8bae1dSRodney W. Grimes 	case ENOBUFS:
1459df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1460df8bae1dSRodney W. Grimes 		code = 0;
1461df8bae1dSRodney W. Grimes 		break;
1462df8bae1dSRodney W. Grimes 	}
1463df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1464df8bae1dSRodney W. Grimes }
1465df8bae1dSRodney W. Grimes 
146682c23ebaSBill Fenner void
146782c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
146882c23ebaSBill Fenner 	register struct inpcb *inp;
146982c23ebaSBill Fenner 	register struct mbuf **mp;
147082c23ebaSBill Fenner 	register struct ip *ip;
147182c23ebaSBill Fenner 	register struct mbuf *m;
147282c23ebaSBill Fenner {
147382c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
147482c23ebaSBill Fenner 		struct timeval tv;
147582c23ebaSBill Fenner 
147682c23ebaSBill Fenner 		microtime(&tv);
147782c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
147882c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
147982c23ebaSBill Fenner 		if (*mp)
148082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
148182c23ebaSBill Fenner 	}
148282c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
148382c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
148482c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
148582c23ebaSBill Fenner 		if (*mp)
148682c23ebaSBill Fenner 			mp = &(*mp)->m_next;
148782c23ebaSBill Fenner 	}
148882c23ebaSBill Fenner #ifdef notyet
148982c23ebaSBill Fenner 	/* XXX
149082c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
149182c23ebaSBill Fenner 	 * than they already were.
149282c23ebaSBill Fenner 	 */
149382c23ebaSBill Fenner 	/* options were tossed already */
149482c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
149582c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
149682c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
149782c23ebaSBill Fenner 		if (*mp)
149882c23ebaSBill Fenner 			mp = &(*mp)->m_next;
149982c23ebaSBill Fenner 	}
150082c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
150182c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
150282c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
150382c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
150482c23ebaSBill Fenner 		if (*mp)
150582c23ebaSBill Fenner 			mp = &(*mp)->m_next;
150682c23ebaSBill Fenner 	}
150782c23ebaSBill Fenner #endif
150882c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
1509d314ad7bSJulian Elischer 		struct ifnet *ifp;
1510d314ad7bSJulian Elischer 		struct sdlbuf {
151182c23ebaSBill Fenner 			struct sockaddr_dl sdl;
1512d314ad7bSJulian Elischer 			u_char	pad[32];
1513d314ad7bSJulian Elischer 		} sdlbuf;
1514d314ad7bSJulian Elischer 		struct sockaddr_dl *sdp;
1515d314ad7bSJulian Elischer 		struct sockaddr_dl *sdl2 = &sdlbuf.sdl;
151682c23ebaSBill Fenner 
1517d314ad7bSJulian Elischer 		if (((ifp = m->m_pkthdr.rcvif))
1518d314ad7bSJulian Elischer 		&& ( ifp->if_index && (ifp->if_index <= if_index))) {
1519d314ad7bSJulian Elischer 			sdp = (struct sockaddr_dl *)(ifnet_addrs
1520d314ad7bSJulian Elischer 					[ifp->if_index - 1]->ifa_addr);
1521d314ad7bSJulian Elischer 			/*
1522d314ad7bSJulian Elischer 			 * Change our mind and don't try copy.
1523d314ad7bSJulian Elischer 			 */
1524d314ad7bSJulian Elischer 			if ((sdp->sdl_family != AF_LINK)
1525d314ad7bSJulian Elischer 			|| (sdp->sdl_len > sizeof(sdlbuf))) {
1526d314ad7bSJulian Elischer 				goto makedummy;
1527d314ad7bSJulian Elischer 			}
1528d314ad7bSJulian Elischer 			bcopy(sdp, sdl2, sdp->sdl_len);
1529d314ad7bSJulian Elischer 		} else {
1530d314ad7bSJulian Elischer makedummy:
1531d314ad7bSJulian Elischer 			sdl2->sdl_len
1532d314ad7bSJulian Elischer 				= offsetof(struct sockaddr_dl, sdl_data[0]);
1533d314ad7bSJulian Elischer 			sdl2->sdl_family = AF_LINK;
1534d314ad7bSJulian Elischer 			sdl2->sdl_index = 0;
1535d314ad7bSJulian Elischer 			sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0;
1536d314ad7bSJulian Elischer 		}
1537d314ad7bSJulian Elischer 		*mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len,
153882c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
153982c23ebaSBill Fenner 		if (*mp)
154082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
154182c23ebaSBill Fenner 	}
154282c23ebaSBill Fenner }
154382c23ebaSBill Fenner 
1544df8bae1dSRodney W. Grimes int
1545f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1546f0068c4aSGarrett Wollman {
1547f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1548f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1549f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1550f0068c4aSGarrett Wollman 
1551f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1552f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1553f0068c4aSGarrett Wollman 
1554f0068c4aSGarrett Wollman 	ip_rsvpd = so;
15551c5de19aSGarrett Wollman 	/*
15561c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
15571c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
15581c5de19aSGarrett Wollman 	 */
15591c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
15601c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
15611c5de19aSGarrett Wollman 		rsvp_on++;
15621c5de19aSGarrett Wollman 	}
1563f0068c4aSGarrett Wollman 
1564f0068c4aSGarrett Wollman 	return 0;
1565f0068c4aSGarrett Wollman }
1566f0068c4aSGarrett Wollman 
1567f0068c4aSGarrett Wollman int
1568f0068c4aSGarrett Wollman ip_rsvp_done(void)
1569f0068c4aSGarrett Wollman {
1570f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
15711c5de19aSGarrett Wollman 	/*
15721c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
15731c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
15741c5de19aSGarrett Wollman 	 */
15751c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
15761c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
15771c5de19aSGarrett Wollman 		rsvp_on--;
15781c5de19aSGarrett Wollman 	}
1579f0068c4aSGarrett Wollman 	return 0;
1580f0068c4aSGarrett Wollman }
1581