xref: /freebsd/sys/netinet/ip_input.c (revision 1b968362aa997224cbd832b868d8fdc495b2c0f6)
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
341b968362SDag-Erling Smørgrav  *	$Id: ip_input.c,v 1.114 1999/02/09 16:55:46 wollman Exp $
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
3758938916SGarrett Wollman #define	_IP_VHL
3858938916SGarrett Wollman 
39e4f4247aSEivind Eklund #include "opt_bootp.h"
4074a9466cSGary Palmer #include "opt_ipfw.h"
41b715f178SLuigi Rizzo #include "opt_ipdn.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>
50b715f178SLuigi Rizzo #include <sys/malloc.h>
51df8bae1dSRodney W. Grimes #include <sys/domain.h>
52df8bae1dSRodney W. Grimes #include <sys/protosw.h>
53df8bae1dSRodney W. Grimes #include <sys/socket.h>
54df8bae1dSRodney W. Grimes #include <sys/time.h>
55df8bae1dSRodney W. Grimes #include <sys/kernel.h>
561025071fSGarrett Wollman #include <sys/syslog.h>
57b5e8ce9fSBruce Evans #include <sys/sysctl.h>
58df8bae1dSRodney W. Grimes 
59df8bae1dSRodney W. Grimes #include <net/if.h>
60d314ad7bSJulian Elischer #include <net/if_var.h>
6182c23ebaSBill Fenner #include <net/if_dl.h>
62df8bae1dSRodney W. Grimes #include <net/route.h>
63748e0b0aSGarrett Wollman #include <net/netisr.h>
64df8bae1dSRodney W. Grimes 
65df8bae1dSRodney W. Grimes #include <netinet/in.h>
66df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
67b5e8ce9fSBruce Evans #include <netinet/in_var.h>
68df8bae1dSRodney W. Grimes #include <netinet/ip.h>
69df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
70df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
71df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
7258938916SGarrett Wollman #include <machine/in_cksum.h>
73df8bae1dSRodney W. Grimes 
74f0068c4aSGarrett Wollman #include <sys/socketvar.h>
756ddbf1e2SGary Palmer 
766ddbf1e2SGary Palmer #ifdef IPFIREWALL
776ddbf1e2SGary Palmer #include <netinet/ip_fw.h>
786ddbf1e2SGary Palmer #endif
796ddbf1e2SGary Palmer 
80b715f178SLuigi Rizzo #ifdef DUMMYNET
81b715f178SLuigi Rizzo #include <netinet/ip_dummynet.h>
82b715f178SLuigi Rizzo #endif
83b715f178SLuigi Rizzo 
841c5de19aSGarrett Wollman int rsvp_on = 0;
85f708ef1bSPoul-Henning Kamp static int ip_rsvp_on;
86f0068c4aSGarrett Wollman struct socket *ip_rsvpd;
87f0068c4aSGarrett Wollman 
881f91d8c5SDavid Greenman int	ipforwarding = 0;
890312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW,
900312fbe9SPoul-Henning Kamp 	&ipforwarding, 0, "");
910312fbe9SPoul-Henning Kamp 
92d4fb926cSGarrett Wollman static int	ipsendredirects = 1; /* XXX */
930312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW,
940312fbe9SPoul-Henning Kamp 	&ipsendredirects, 0, "");
950312fbe9SPoul-Henning Kamp 
96df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
970312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW,
980312fbe9SPoul-Henning Kamp 	&ip_defttl, 0, "");
990312fbe9SPoul-Henning Kamp 
1000312fbe9SPoul-Henning Kamp static int	ip_dosourceroute = 0;
1010312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
1020312fbe9SPoul-Henning Kamp 	&ip_dosourceroute, 0, "");
1034fce5804SGuido van Rooij 
1044fce5804SGuido van Rooij static int	ip_acceptsourceroute = 0;
1054fce5804SGuido van Rooij SYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute,
1064fce5804SGuido van Rooij 	CTLFLAG_RW, &ip_acceptsourceroute, 0, "");
107df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1080312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
109df8bae1dSRodney W. Grimes #endif
110df8bae1dSRodney W. Grimes 
111df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
112df8bae1dSRodney W. Grimes extern	struct protosw inetsw[];
113df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
1140312fbe9SPoul-Henning Kamp static int	ipqmaxlen = IFQ_MAXLEN;
11559562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; /* first inet address */
116df8bae1dSRodney W. Grimes struct	ifqueue ipintrq;
1170312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RD,
1180312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_maxlen, 0, "");
1190312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1200312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_drops, 0, "");
121df8bae1dSRodney W. Grimes 
122f23b4c91SGarrett Wollman struct ipstat ipstat;
1236fce01c9SGarrett Wollman SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RD,
1246fce01c9SGarrett Wollman 	&ipstat, ipstat, "");
125194a213eSAndrey A. Chernov 
126194a213eSAndrey A. Chernov /* Packet reassembly stuff */
127194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
128194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
129194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
130194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
131831a80b0SMatthew Dillon 	(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
132194a213eSAndrey A. Chernov 
133194a213eSAndrey A. Chernov static struct ipq ipq[IPREASS_NHASH];
134194a213eSAndrey A. Chernov static int    nipq = 0;         /* total # of reass queues */
135194a213eSAndrey A. Chernov static int    maxnipq;
136f23b4c91SGarrett Wollman 
1370312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1380312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1390312fbe9SPoul-Henning Kamp 	&ip_mtu, 0, "");
1400312fbe9SPoul-Henning Kamp #endif
1410312fbe9SPoul-Henning Kamp 
1421b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
1431b968362SDag-Erling Smørgrav static int	ipstealth = 0;
1441b968362SDag-Erling Smørgrav SYSCTL_INT(_net_inet_ip, OID_AUTO, stealth, CTLFLAG_RW,
1451b968362SDag-Erling Smørgrav     &ipstealth, 0, "");
1461b968362SDag-Erling Smørgrav #endif
1471b968362SDag-Erling Smørgrav 
14858938916SGarrett Wollman #if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1
14958938916SGarrett Wollman #undef COMPAT_IPFW
15058938916SGarrett Wollman #define COMPAT_IPFW 1
15158938916SGarrett Wollman #else
15258938916SGarrett Wollman #undef COMPAT_IPFW
15358938916SGarrett Wollman #endif
15458938916SGarrett Wollman 
15558938916SGarrett Wollman #ifdef COMPAT_IPFW
156cfe8b629SGarrett Wollman 
157cfe8b629SGarrett Wollman #include <netinet/ip_fw.h>
158cfe8b629SGarrett Wollman 
15923bf9953SPoul-Henning Kamp /* Firewall hooks */
16023bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
16123bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
162e7319babSPoul-Henning Kamp 
163b715f178SLuigi Rizzo #ifdef DUMMYNET
164b715f178SLuigi Rizzo ip_dn_ctl_t *ip_dn_ctl_ptr;
165b715f178SLuigi Rizzo #endif
166b715f178SLuigi Rizzo 
167fed1c7e9SSøren Schmidt /* IP Network Address Translation (NAT) hooks */
168fed1c7e9SSøren Schmidt ip_nat_t *ip_nat_ptr;
169fed1c7e9SSøren Schmidt ip_nat_ctl_t *ip_nat_ctl_ptr;
17058938916SGarrett Wollman #endif
171fed1c7e9SSøren Schmidt 
172afed1b49SDarren Reed #if defined(IPFILTER_LKM) || defined(IPFILTER)
1731ee25934SPeter Wemm int iplattach __P((void));
174afed1b49SDarren Reed int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)) = NULL;
175afed1b49SDarren Reed #endif
176afed1b49SDarren Reed 
177afed1b49SDarren Reed 
178e7319babSPoul-Henning Kamp /*
179df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
180df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
181df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
182df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
183df8bae1dSRodney W. Grimes  * to us.
184df8bae1dSRodney W. Grimes  */
1850312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
186df8bae1dSRodney W. Grimes static	struct ip_srcrt {
187df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
188df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
189df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
190df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
191df8bae1dSRodney W. Grimes } ip_srcrt;
192df8bae1dSRodney W. Grimes 
19393e0e116SJulian Elischer #ifdef IPDIVERT
19493e0e116SJulian Elischer /*
19593e0e116SJulian Elischer  * Shared variable between ip_input() and ip_reass() to communicate
19693e0e116SJulian Elischer  * about which packets, once assembled from fragments, get diverted,
19793e0e116SJulian Elischer  * and to which port.
19893e0e116SJulian Elischer  */
19993e0e116SJulian Elischer static u_short	frag_divert_port;
20093e0e116SJulian Elischer #endif
20193e0e116SJulian Elischer 
202f9e354dfSJulian Elischer struct sockaddr_in *ip_fw_fwd_addr;
203f9e354dfSJulian Elischer 
204df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr));
2050312fbe9SPoul-Henning Kamp static int	 ip_dooptions __P((struct mbuf *));
2060312fbe9SPoul-Henning Kamp static void	 ip_forward __P((struct mbuf *, int));
2070312fbe9SPoul-Henning Kamp static void	 ip_freef __P((struct ipq *));
2080312fbe9SPoul-Henning Kamp static struct ip *
2096effc713SDoug Rabson 	 ip_reass __P((struct mbuf *, struct ipq *, struct ipq *));
2100312fbe9SPoul-Henning Kamp static struct in_ifaddr *
2110312fbe9SPoul-Henning Kamp 	 ip_rtaddr __P((struct in_addr));
2120312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
213df8bae1dSRodney W. Grimes /*
214df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
215df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
216df8bae1dSRodney W. Grimes  */
217df8bae1dSRodney W. Grimes void
218df8bae1dSRodney W. Grimes ip_init()
219df8bae1dSRodney W. Grimes {
220df8bae1dSRodney W. Grimes 	register struct protosw *pr;
221df8bae1dSRodney W. Grimes 	register int i;
222df8bae1dSRodney W. Grimes 
22359562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
224df8bae1dSRodney W. Grimes 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
225df8bae1dSRodney W. Grimes 	if (pr == 0)
226df8bae1dSRodney W. Grimes 		panic("ip_init");
227df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
228df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
229df8bae1dSRodney W. Grimes 	for (pr = inetdomain.dom_protosw;
230df8bae1dSRodney W. Grimes 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
231df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
232df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
233df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
234194a213eSAndrey A. Chernov 
235194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
236194a213eSAndrey A. Chernov 	    ipq[i].next = ipq[i].prev = &ipq[i];
237194a213eSAndrey A. Chernov 
238194a213eSAndrey A. Chernov 	maxnipq = nmbclusters/4;
239194a213eSAndrey A. Chernov 
240227ee8a1SPoul-Henning Kamp 	ip_id = time_second & 0xffff;
241df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
242b715f178SLuigi Rizzo #ifdef DUMMYNET
243b715f178SLuigi Rizzo 	ip_dn_init();
244b715f178SLuigi Rizzo #endif
245fed1c7e9SSøren Schmidt #ifdef IPNAT
246fed1c7e9SSøren Schmidt         ip_nat_init();
247fed1c7e9SSøren Schmidt #endif
2481ee25934SPeter Wemm #ifdef IPFILTER
2491ee25934SPeter Wemm         iplattach();
2501ee25934SPeter Wemm #endif
251fed1c7e9SSøren Schmidt 
252df8bae1dSRodney W. Grimes }
253df8bae1dSRodney W. Grimes 
2540312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
255f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
256df8bae1dSRodney W. Grimes 
257df8bae1dSRodney W. Grimes /*
258df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
259df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
260df8bae1dSRodney W. Grimes  */
261c67b1d17SGarrett Wollman void
262c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
263df8bae1dSRodney W. Grimes {
26423bf9953SPoul-Henning Kamp 	struct ip *ip;
26523bf9953SPoul-Henning Kamp 	struct ipq *fp;
26623bf9953SPoul-Henning Kamp 	struct in_ifaddr *ia;
2676effc713SDoug Rabson 	int    i, hlen, mff;
26847c861ecSBrian Somers 	u_short sum;
269b715f178SLuigi Rizzo #ifndef IPDIVERT /* dummy variable for the firewall code to play with */
270b715f178SLuigi Rizzo         u_short ip_divert_cookie = 0 ;
271b715f178SLuigi Rizzo #endif
272b715f178SLuigi Rizzo #ifdef COMPAT_IPFW
273b715f178SLuigi Rizzo 	struct ip_fw_chain *rule = NULL ;
274b715f178SLuigi Rizzo #endif
275b715f178SLuigi Rizzo 
276b715f178SLuigi Rizzo #if defined(IPFIREWALL) && defined(DUMMYNET)
277b715f178SLuigi Rizzo         /*
278b715f178SLuigi Rizzo          * dummynet packet are prepended a vestigial mbuf with
279b715f178SLuigi Rizzo          * m_type = MT_DUMMYNET and m_data pointing to the matching
280b715f178SLuigi Rizzo          * rule.
281b715f178SLuigi Rizzo          */
282b715f178SLuigi Rizzo         if (m->m_type == MT_DUMMYNET) {
283b715f178SLuigi Rizzo             struct mbuf *m0 = m ;
284b715f178SLuigi Rizzo             rule = (struct ip_fw_chain *)(m->m_data) ;
285b715f178SLuigi Rizzo             m = m->m_next ;
286b715f178SLuigi Rizzo             free(m0, M_IPFW);
287b715f178SLuigi Rizzo             ip = mtod(m, struct ip *);
288b715f178SLuigi Rizzo             hlen = IP_VHL_HL(ip->ip_vhl) << 2;
289b715f178SLuigi Rizzo             goto iphack ;
290b715f178SLuigi Rizzo         } else
291b715f178SLuigi Rizzo             rule = NULL ;
292b715f178SLuigi Rizzo #endif
293df8bae1dSRodney W. Grimes 
294df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
295ed7509acSJulian Elischer 	if (m == NULL || (m->m_flags & M_PKTHDR) == 0)
29658938916SGarrett Wollman 		panic("ip_input no HDR");
297df8bae1dSRodney W. Grimes #endif
298df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
29958938916SGarrett Wollman 
30058938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
30158938916SGarrett Wollman 		goto tooshort;
30258938916SGarrett Wollman 
303df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
304df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
305df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
306c67b1d17SGarrett Wollman 		return;
307df8bae1dSRodney W. Grimes 	}
308df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
30958938916SGarrett Wollman 
31058938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
311df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
312df8bae1dSRodney W. Grimes 		goto bad;
313df8bae1dSRodney W. Grimes 	}
31458938916SGarrett Wollman 
31558938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
316df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
317df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
318df8bae1dSRodney W. Grimes 		goto bad;
319df8bae1dSRodney W. Grimes 	}
320df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
321df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
322df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
323c67b1d17SGarrett Wollman 			return;
324df8bae1dSRodney W. Grimes 		}
325df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
326df8bae1dSRodney W. Grimes 	}
32758938916SGarrett Wollman 	if (hlen == sizeof(struct ip)) {
32847c861ecSBrian Somers 		sum = in_cksum_hdr(ip);
32958938916SGarrett Wollman 	} else {
33047c861ecSBrian Somers 		sum = in_cksum(m, hlen);
33158938916SGarrett Wollman 	}
33247c861ecSBrian Somers 	if (sum) {
333df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
334df8bae1dSRodney W. Grimes 		goto bad;
335df8bae1dSRodney W. Grimes 	}
336df8bae1dSRodney W. Grimes 
337df8bae1dSRodney W. Grimes 	/*
338df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
339df8bae1dSRodney W. Grimes 	 */
340df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
341df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
342df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
343df8bae1dSRodney W. Grimes 		goto bad;
344df8bae1dSRodney W. Grimes 	}
345df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_id);
346df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
347df8bae1dSRodney W. Grimes 
348df8bae1dSRodney W. Grimes 	/*
349df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
350df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
351df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
352df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
353df8bae1dSRodney W. Grimes 	 */
354df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
35558938916SGarrett Wollman tooshort:
356df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
357df8bae1dSRodney W. Grimes 		goto bad;
358df8bae1dSRodney W. Grimes 	}
359df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
360df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
361df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
362df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
363df8bae1dSRodney W. Grimes 		} else
364df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
365df8bae1dSRodney W. Grimes 	}
3664dd1662bSUgen J.S. Antsilevich 	/*
3674dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3684dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
3694dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
3704dd1662bSUgen J.S. Antsilevich 	 * deals with it.
37193e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
372fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
373b715f178SLuigi Rizzo 	 * - Pipe: pass pkt through dummynet.
3744dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
3754dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
3764dd1662bSUgen J.S. Antsilevich  	 */
377b715f178SLuigi Rizzo 
378dee383e0SEivind Eklund #if defined(IPFIREWALL) && defined(DUMMYNET)
379b715f178SLuigi Rizzo iphack:
380dee383e0SEivind Eklund #endif
381beec8214SDarren Reed #if defined(IPFILTER) || defined(IPFILTER_LKM)
382beec8214SDarren Reed 	/*
383beec8214SDarren Reed 	 * Check if we want to allow this packet to be processed.
384beec8214SDarren Reed 	 * Consider it to be bad if not.
385beec8214SDarren Reed 	 */
3861ee25934SPeter Wemm 	if (fr_checkp) {
387beec8214SDarren Reed 		struct	mbuf	*m1 = m;
388df8bae1dSRodney W. Grimes 
389beec8214SDarren Reed 		if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m1) || !m1)
390beec8214SDarren Reed 			return;
391beec8214SDarren Reed 		ip = mtod(m = m1, struct ip *);
392beec8214SDarren Reed 	}
393beec8214SDarren Reed #endif
39458938916SGarrett Wollman #ifdef COMPAT_IPFW
39593e0e116SJulian Elischer 	if (ip_fw_chk_ptr) {
396f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
397f9e354dfSJulian Elischer 		/*
398f9e354dfSJulian Elischer 		 * If we've been forwarded from the output side, then
399f9e354dfSJulian Elischer 		 * skip the firewall a second time
400f9e354dfSJulian Elischer 		 */
401f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr)
402f9e354dfSJulian Elischer 			goto ours;
403f9e354dfSJulian Elischer #endif	/* IPFIREWALL_FORWARD */
404b715f178SLuigi Rizzo 		i = (*ip_fw_chk_ptr)(&ip, hlen, NULL, &ip_divert_cookie,
405b715f178SLuigi Rizzo 					&m, &rule, &ip_fw_fwd_addr);
406f9e354dfSJulian Elischer 		/*
407b715f178SLuigi Rizzo 		 * see the comment in ip_output for the return values
408b715f178SLuigi Rizzo 		 * produced by the firewall.
409f9e354dfSJulian Elischer 		 */
410b715f178SLuigi Rizzo 		if (!m) /* packet discarded by firewall */
411b715f178SLuigi Rizzo 			return ;
412b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr == NULL) /* common case */
413b715f178SLuigi Rizzo 			goto pass ;
414b715f178SLuigi Rizzo #ifdef DUMMYNET
415b715f178SLuigi Rizzo                 if (i & 0x10000) {
416b715f178SLuigi Rizzo                         /* send packet to the appropriate pipe */
417b715f178SLuigi Rizzo                         dummynet_io(i&0xffff,DN_TO_IP_IN,m,NULL,NULL,0, rule);
418e4676ba6SJulian Elischer 			return ;
41993e0e116SJulian Elischer 		}
420b715f178SLuigi Rizzo #endif
421b715f178SLuigi Rizzo #ifdef IPDIVERT
422b715f178SLuigi Rizzo 		if (i > 0 && i < 0x10000) {
423b715f178SLuigi Rizzo 			/* Divert packet */
424b715f178SLuigi Rizzo 			frag_divert_port = i & 0xffff ;
425b715f178SLuigi Rizzo 			goto ours;
426b715f178SLuigi Rizzo 		}
427b715f178SLuigi Rizzo #endif
428b715f178SLuigi Rizzo #ifdef IPFIREWALL_FORWARD
429b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr != NULL)
430b715f178SLuigi Rizzo 			goto pass ;
431b715f178SLuigi Rizzo #endif
432b715f178SLuigi Rizzo 		/*
433b715f178SLuigi Rizzo 		 * if we get here, the packet must be dropped
434b715f178SLuigi Rizzo 		 */
435b715f178SLuigi Rizzo 			m_freem(m);
436b715f178SLuigi Rizzo 			return;
437b715f178SLuigi Rizzo 	}
438b715f178SLuigi Rizzo pass:
439100ba1a6SJordan K. Hubbard 
4406713d4a7SSøren Schmidt         if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN))
441fed1c7e9SSøren Schmidt 		return;
442f9e354dfSJulian Elischer #endif	/* !COMPAT_IPFW */
443fed1c7e9SSøren Schmidt 
444df8bae1dSRodney W. Grimes 	/*
445df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
446df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
447df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
448df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
449df8bae1dSRodney W. Grimes 	 */
450df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
451df8bae1dSRodney W. Grimes 	if (hlen > sizeof (struct ip) && ip_dooptions(m))
452c67b1d17SGarrett Wollman 		return;
453df8bae1dSRodney W. Grimes 
454f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
455f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
456f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
457f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
458f0068c4aSGarrett Wollman 	 * grabbing the packet.
459f0068c4aSGarrett Wollman          */
4601c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
461f0068c4aSGarrett Wollman 		goto ours;
462f0068c4aSGarrett Wollman 
463df8bae1dSRodney W. Grimes 	/*
464df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
465cc766e04SGarrett Wollman 	 * If we don't have any addresses, assume any unicast packet
466cc766e04SGarrett Wollman 	 * we receive might be for us (and let the upper layers deal
467cc766e04SGarrett Wollman 	 * with it).
468df8bae1dSRodney W. Grimes 	 */
469cc766e04SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead) &&
470cc766e04SGarrett Wollman 	    (m->m_flags & (M_MCAST|M_BCAST)) == 0)
471cc766e04SGarrett Wollman 		goto ours;
472cc766e04SGarrett Wollman 
473d4295c32SJulian Elischer 	for (ia = TAILQ_FIRST(&in_ifaddrhead); ia;
474f9e354dfSJulian Elischer 					ia = TAILQ_NEXT(ia, ia_link)) {
475df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
476df8bae1dSRodney W. Grimes 
477df8bae1dSRodney W. Grimes 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
478df8bae1dSRodney W. Grimes 			goto ours;
479432aad0eSTor Egge #ifdef BOOTP_COMPAT
480432aad0eSTor Egge 		if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
481432aad0eSTor Egge 			goto ours;
482432aad0eSTor Egge #endif
483f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
484f9e354dfSJulian Elischer 		/*
485f9e354dfSJulian Elischer 		 * If the addr to forward to is one of ours, we pretend to
486f9e354dfSJulian Elischer 		 * be the destination for this packet.
487f9e354dfSJulian Elischer 		 */
488f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr != NULL &&
489f9e354dfSJulian Elischer 			IA_SIN(ia)->sin_addr.s_addr ==
490f9e354dfSJulian Elischer 					 ip_fw_fwd_addr->sin_addr.s_addr)
491f9e354dfSJulian Elischer 			goto ours;
492f9e354dfSJulian Elischer #endif
4936ed666afSPoul-Henning Kamp 		if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
494df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
495df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr)
496df8bae1dSRodney W. Grimes 				goto ours;
497df8bae1dSRodney W. Grimes 			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
498df8bae1dSRodney W. Grimes 				goto ours;
499df8bae1dSRodney W. Grimes 		}
500df8bae1dSRodney W. Grimes 	}
501df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
502df8bae1dSRodney W. Grimes 		struct in_multi *inm;
503df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
504df8bae1dSRodney W. Grimes 			/*
505df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
506df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
507df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
508df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
509df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
510df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
511df8bae1dSRodney W. Grimes 			 *
512df8bae1dSRodney W. Grimes 			 * (The IP ident field is put in the same byte order
513df8bae1dSRodney W. Grimes 			 * as expected when ip_mforward() is called from
514df8bae1dSRodney W. Grimes 			 * ip_output().)
515df8bae1dSRodney W. Grimes 			 */
516df8bae1dSRodney W. Grimes 			ip->ip_id = htons(ip->ip_id);
517f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
518df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
519df8bae1dSRodney W. Grimes 				m_freem(m);
520c67b1d17SGarrett Wollman 				return;
521df8bae1dSRodney W. Grimes 			}
522df8bae1dSRodney W. Grimes 			ip->ip_id = ntohs(ip->ip_id);
523df8bae1dSRodney W. Grimes 
524df8bae1dSRodney W. Grimes 			/*
525df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
526df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
527df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
528df8bae1dSRodney W. Grimes 			 */
529df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
530df8bae1dSRodney W. Grimes 				goto ours;
531df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
532df8bae1dSRodney W. Grimes 		}
533df8bae1dSRodney W. Grimes 		/*
534df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
535df8bae1dSRodney W. Grimes 		 * arrival interface.
536df8bae1dSRodney W. Grimes 		 */
537df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
538df8bae1dSRodney W. Grimes 		if (inm == NULL) {
53982c39223SGarrett Wollman 			ipstat.ips_notmember++;
540df8bae1dSRodney W. Grimes 			m_freem(m);
541c67b1d17SGarrett Wollman 			return;
542df8bae1dSRodney W. Grimes 		}
543df8bae1dSRodney W. Grimes 		goto ours;
544df8bae1dSRodney W. Grimes 	}
545df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
546df8bae1dSRodney W. Grimes 		goto ours;
547df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
548df8bae1dSRodney W. Grimes 		goto ours;
549df8bae1dSRodney W. Grimes 
550df8bae1dSRodney W. Grimes 	/*
551df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
552df8bae1dSRodney W. Grimes 	 */
553df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
554df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
555df8bae1dSRodney W. Grimes 		m_freem(m);
556df8bae1dSRodney W. Grimes 	} else
557df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
558c67b1d17SGarrett Wollman 	return;
559df8bae1dSRodney W. Grimes 
560df8bae1dSRodney W. Grimes ours:
561100ba1a6SJordan K. Hubbard 
56263f8d699SJordan K. Hubbard 	/*
563df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
564df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
565df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
566df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
567df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
568df8bae1dSRodney W. Grimes 	 */
569c383a33fSDima Ruban 	if (ip->ip_off & (IP_MF | IP_OFFMASK | IP_RF)) {
570df8bae1dSRodney W. Grimes 		if (m->m_flags & M_EXT) {		/* XXX */
571af38c68cSLuigi Rizzo 			if ((m = m_pullup(m, hlen)) == 0) {
572df8bae1dSRodney W. Grimes 				ipstat.ips_toosmall++;
57393e0e116SJulian Elischer #ifdef IPDIVERT
57493e0e116SJulian Elischer 				frag_divert_port = 0;
575c977d4c7SJulian Elischer 				ip_divert_cookie = 0;
57693e0e116SJulian Elischer #endif
577c67b1d17SGarrett Wollman 				return;
578df8bae1dSRodney W. Grimes 			}
579df8bae1dSRodney W. Grimes 			ip = mtod(m, struct ip *);
580df8bae1dSRodney W. Grimes 		}
581194a213eSAndrey A. Chernov 		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
582df8bae1dSRodney W. Grimes 		/*
583df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
584df8bae1dSRodney W. Grimes 		 * of this datagram.
585df8bae1dSRodney W. Grimes 		 */
586194a213eSAndrey A. Chernov 		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
587df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
588df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
589df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
590df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
591df8bae1dSRodney W. Grimes 				goto found;
592df8bae1dSRodney W. Grimes 
593194a213eSAndrey A. Chernov 		fp = 0;
594194a213eSAndrey A. Chernov 
595194a213eSAndrey A. Chernov 		/* check if there's a place for the new queue */
596194a213eSAndrey A. Chernov 		if (nipq > maxnipq) {
597194a213eSAndrey A. Chernov 		    /*
598194a213eSAndrey A. Chernov 		     * drop something from the tail of the current queue
599194a213eSAndrey A. Chernov 		     * before proceeding further
600194a213eSAndrey A. Chernov 		     */
601194a213eSAndrey A. Chernov 		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */
602194a213eSAndrey A. Chernov 			for (i = 0; i < IPREASS_NHASH; i++) {
603194a213eSAndrey A. Chernov 			    if (ipq[i].prev != &ipq[i]) {
604194a213eSAndrey A. Chernov 				ip_freef(ipq[i].prev);
605194a213eSAndrey A. Chernov 				break;
606194a213eSAndrey A. Chernov 			    }
607194a213eSAndrey A. Chernov 			}
608194a213eSAndrey A. Chernov 		    } else
609194a213eSAndrey A. Chernov 			ip_freef(ipq[sum].prev);
610194a213eSAndrey A. Chernov 		}
611194a213eSAndrey A. Chernov found:
612df8bae1dSRodney W. Grimes 		/*
613df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
614df8bae1dSRodney W. Grimes 		 * set ip_mff if more fragments are expected,
615df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
616df8bae1dSRodney W. Grimes 		 */
617df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
6186effc713SDoug Rabson 		mff = (ip->ip_off & IP_MF) != 0;
6196effc713SDoug Rabson 		if (mff) {
6206effc713SDoug Rabson 		        /*
6216effc713SDoug Rabson 		         * Make sure that fragments have a data length
6226effc713SDoug Rabson 			 * that's a non-zero multiple of 8 bytes.
6236effc713SDoug Rabson 		         */
6246effc713SDoug Rabson 			if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
6256effc713SDoug Rabson 				ipstat.ips_toosmall++; /* XXX */
6266effc713SDoug Rabson 				goto bad;
6276effc713SDoug Rabson 			}
6286effc713SDoug Rabson 			m->m_flags |= M_FRAG;
6296effc713SDoug Rabson 		}
630df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
631df8bae1dSRodney W. Grimes 
632df8bae1dSRodney W. Grimes 		/*
633df8bae1dSRodney W. Grimes 		 * If datagram marked as having more fragments
634df8bae1dSRodney W. Grimes 		 * or if this is not the first fragment,
635df8bae1dSRodney W. Grimes 		 * attempt reassembly; if it succeeds, proceed.
636df8bae1dSRodney W. Grimes 		 */
6376effc713SDoug Rabson 		if (mff || ip->ip_off) {
638df8bae1dSRodney W. Grimes 			ipstat.ips_fragments++;
6396effc713SDoug Rabson 			m->m_pkthdr.header = ip;
6406effc713SDoug Rabson 			ip = ip_reass(m, fp, &ipq[sum]);
641f9e354dfSJulian Elischer 			if (ip == 0) {
642f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
643f9e354dfSJulian Elischer 				ip_fw_fwd_addr = NULL;
644f9e354dfSJulian Elischer #endif
645c67b1d17SGarrett Wollman 				return;
646f9e354dfSJulian Elischer 			}
64781aee63dSPoul-Henning Kamp 			/* Get the length of the reassembled packets header */
64881aee63dSPoul-Henning Kamp 			hlen = IP_VHL_HL(ip->ip_vhl) << 2;
649df8bae1dSRodney W. Grimes 			ipstat.ips_reassembled++;
650df8bae1dSRodney W. Grimes 			m = dtom(ip);
651af782f1cSBrian Somers #ifdef IPDIVERT
652af782f1cSBrian Somers 			if (frag_divert_port) {
653af782f1cSBrian Somers 				ip->ip_len += hlen;
654af782f1cSBrian Somers 				HTONS(ip->ip_len);
655af782f1cSBrian Somers 				HTONS(ip->ip_off);
656af782f1cSBrian Somers 				HTONS(ip->ip_id);
657af782f1cSBrian Somers 				ip->ip_sum = 0;
658af782f1cSBrian Somers 				ip->ip_sum = in_cksum_hdr(ip);
659af782f1cSBrian Somers 				NTOHS(ip->ip_id);
660af782f1cSBrian Somers 				NTOHS(ip->ip_off);
661af782f1cSBrian Somers 				NTOHS(ip->ip_len);
662af782f1cSBrian Somers 				ip->ip_len -= hlen;
663af782f1cSBrian Somers 			}
664af782f1cSBrian Somers #endif
665df8bae1dSRodney W. Grimes 		} else
666df8bae1dSRodney W. Grimes 			if (fp)
667df8bae1dSRodney W. Grimes 				ip_freef(fp);
668df8bae1dSRodney W. Grimes 	} else
669df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
670df8bae1dSRodney W. Grimes 
67193e0e116SJulian Elischer #ifdef IPDIVERT
67293e0e116SJulian Elischer 	/*
673e4676ba6SJulian Elischer 	 * Divert reassembled packets to the divert protocol if required
674efe39c6aSJulian Elischer 	 *  If divert port is null then cookie should be too,
675efe39c6aSJulian Elischer 	 * so we shouldn't need to clear them here. Assume ip_divert does so.
67693e0e116SJulian Elischer 	 */
67793e0e116SJulian Elischer 	if (frag_divert_port) {
678e4676ba6SJulian Elischer 		ipstat.ips_delivered++;
67993e0e116SJulian Elischer 		ip_divert_port = frag_divert_port;
68093e0e116SJulian Elischer 		frag_divert_port = 0;
68193e0e116SJulian Elischer 		(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);
68293e0e116SJulian Elischer 		return;
68393e0e116SJulian Elischer 	}
68479755dc5SJulian Elischer 
68579755dc5SJulian Elischer 	/* Don't let packets divert themselves */
68679755dc5SJulian Elischer 	if (ip->ip_p == IPPROTO_DIVERT) {
68779755dc5SJulian Elischer 		ipstat.ips_noproto++;
68879755dc5SJulian Elischer 		goto bad;
68979755dc5SJulian Elischer 	}
690bb60f459SJulian Elischer 
69193e0e116SJulian Elischer #endif
69293e0e116SJulian Elischer 
693df8bae1dSRodney W. Grimes 	/*
694df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
695df8bae1dSRodney W. Grimes 	 */
696df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
697df8bae1dSRodney W. Grimes 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
698f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
699f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;	/* tcp needed it */
700f9e354dfSJulian Elischer #endif
701c67b1d17SGarrett Wollman 	return;
702df8bae1dSRodney W. Grimes bad:
703f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
704f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;
705f9e354dfSJulian Elischer #endif
706df8bae1dSRodney W. Grimes 	m_freem(m);
707c67b1d17SGarrett Wollman }
708c67b1d17SGarrett Wollman 
709c67b1d17SGarrett Wollman /*
710c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
711c67b1d17SGarrett Wollman  */
712c67b1d17SGarrett Wollman static void
713c67b1d17SGarrett Wollman ipintr(void)
714c67b1d17SGarrett Wollman {
715c67b1d17SGarrett Wollman 	int s;
716c67b1d17SGarrett Wollman 	struct mbuf *m;
717c67b1d17SGarrett Wollman 
718c67b1d17SGarrett Wollman 	while(1) {
719c67b1d17SGarrett Wollman 		s = splimp();
720c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
721c67b1d17SGarrett Wollman 		splx(s);
722c67b1d17SGarrett Wollman 		if (m == 0)
723c67b1d17SGarrett Wollman 			return;
724c67b1d17SGarrett Wollman 		ip_input(m);
725c67b1d17SGarrett Wollman 	}
726df8bae1dSRodney W. Grimes }
727df8bae1dSRodney W. Grimes 
728748e0b0aSGarrett Wollman NETISR_SET(NETISR_IP, ipintr);
729748e0b0aSGarrett Wollman 
730df8bae1dSRodney W. Grimes /*
731df8bae1dSRodney W. Grimes  * Take incoming datagram fragment and try to
732df8bae1dSRodney W. Grimes  * reassemble it into whole datagram.  If a chain for
733df8bae1dSRodney W. Grimes  * reassembly of this datagram already exists, then it
734df8bae1dSRodney W. Grimes  * is given as fp; otherwise have to make a chain.
735df8bae1dSRodney W. Grimes  */
7360312fbe9SPoul-Henning Kamp static struct ip *
7376effc713SDoug Rabson ip_reass(m, fp, where)
7386effc713SDoug Rabson 	register struct mbuf *m;
739df8bae1dSRodney W. Grimes 	register struct ipq *fp;
740194a213eSAndrey A. Chernov 	struct   ipq    *where;
741df8bae1dSRodney W. Grimes {
7426effc713SDoug Rabson 	struct ip *ip = mtod(m, struct ip *);
7436effc713SDoug Rabson 	register struct mbuf *p = 0, *q, *nq;
744df8bae1dSRodney W. Grimes 	struct mbuf *t;
7456effc713SDoug Rabson 	int hlen = IP_VHL_HL(ip->ip_vhl) << 2;
746df8bae1dSRodney W. Grimes 	int i, next;
747df8bae1dSRodney W. Grimes 
748df8bae1dSRodney W. Grimes 	/*
749df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
750df8bae1dSRodney W. Grimes 	 * would confuse code below.
751df8bae1dSRodney W. Grimes 	 */
752df8bae1dSRodney W. Grimes 	m->m_data += hlen;
753df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
754df8bae1dSRodney W. Grimes 
755df8bae1dSRodney W. Grimes 	/*
756df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
757df8bae1dSRodney W. Grimes 	 */
758df8bae1dSRodney W. Grimes 	if (fp == 0) {
759df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
760df8bae1dSRodney W. Grimes 			goto dropfrag;
761df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
762194a213eSAndrey A. Chernov 		insque(fp, where);
763194a213eSAndrey A. Chernov 		nipq++;
764df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
765df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
766df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
7676effc713SDoug Rabson 		fp->ipq_src = ip->ip_src;
7686effc713SDoug Rabson 		fp->ipq_dst = ip->ip_dst;
769af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
770af38c68cSLuigi Rizzo 		m->m_nextpkt = NULL;
77193e0e116SJulian Elischer #ifdef IPDIVERT
77293e0e116SJulian Elischer 		fp->ipq_divert = 0;
773bb60f459SJulian Elischer 		fp->ipq_div_cookie = 0;
77493e0e116SJulian Elischer #endif
775af38c68cSLuigi Rizzo 		goto inserted;
776df8bae1dSRodney W. Grimes 	}
777df8bae1dSRodney W. Grimes 
7786effc713SDoug Rabson #define GETIP(m)	((struct ip*)((m)->m_pkthdr.header))
7796effc713SDoug Rabson 
780df8bae1dSRodney W. Grimes 	/*
781df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
782df8bae1dSRodney W. Grimes 	 */
7836effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
7846effc713SDoug Rabson 		if (GETIP(q)->ip_off > ip->ip_off)
785df8bae1dSRodney W. Grimes 			break;
786df8bae1dSRodney W. Grimes 
787df8bae1dSRodney W. Grimes 	/*
788df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
789df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
790af38c68cSLuigi Rizzo 	 * segment.  If it provides all of our data, drop us, otherwise
791af38c68cSLuigi Rizzo 	 * stick new segment in the proper place.
792df8bae1dSRodney W. Grimes 	 */
7936effc713SDoug Rabson 	if (p) {
7946effc713SDoug Rabson 		i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;
795df8bae1dSRodney W. Grimes 		if (i > 0) {
796df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
797df8bae1dSRodney W. Grimes 				goto dropfrag;
798df8bae1dSRodney W. Grimes 			m_adj(dtom(ip), i);
799df8bae1dSRodney W. Grimes 			ip->ip_off += i;
800df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
801df8bae1dSRodney W. Grimes 		}
802af38c68cSLuigi Rizzo 		m->m_nextpkt = p->m_nextpkt;
803af38c68cSLuigi Rizzo 		p->m_nextpkt = m;
804af38c68cSLuigi Rizzo 	} else {
805af38c68cSLuigi Rizzo 		m->m_nextpkt = fp->ipq_frags;
806af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
807df8bae1dSRodney W. Grimes 	}
808df8bae1dSRodney W. Grimes 
809df8bae1dSRodney W. Grimes 	/*
810df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
811df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
812df8bae1dSRodney W. Grimes 	 */
8136effc713SDoug Rabson 	for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;
814af38c68cSLuigi Rizzo 	     q = nq) {
8156effc713SDoug Rabson 		i = (ip->ip_off + ip->ip_len) -
8166effc713SDoug Rabson 		    GETIP(q)->ip_off;
8176effc713SDoug Rabson 		if (i < GETIP(q)->ip_len) {
8186effc713SDoug Rabson 			GETIP(q)->ip_len -= i;
8196effc713SDoug Rabson 			GETIP(q)->ip_off += i;
8206effc713SDoug Rabson 			m_adj(q, i);
821df8bae1dSRodney W. Grimes 			break;
822df8bae1dSRodney W. Grimes 		}
8236effc713SDoug Rabson 		nq = q->m_nextpkt;
824af38c68cSLuigi Rizzo 		m->m_nextpkt = nq;
8256effc713SDoug Rabson 		m_freem(q);
826df8bae1dSRodney W. Grimes 	}
827df8bae1dSRodney W. Grimes 
828af38c68cSLuigi Rizzo inserted:
82993e0e116SJulian Elischer 
83093e0e116SJulian Elischer #ifdef IPDIVERT
83193e0e116SJulian Elischer 	/*
83293e0e116SJulian Elischer 	 * Any fragment diverting causes the whole packet to divert
83393e0e116SJulian Elischer 	 */
834efe39c6aSJulian Elischer 	if (frag_divert_port) {
83593e0e116SJulian Elischer 		fp->ipq_divert = frag_divert_port;
836c977d4c7SJulian Elischer 		fp->ipq_div_cookie = ip_divert_cookie;
837bb60f459SJulian Elischer 	}
83893e0e116SJulian Elischer 	frag_divert_port = 0;
839c977d4c7SJulian Elischer 	ip_divert_cookie = 0;
84093e0e116SJulian Elischer #endif
84193e0e116SJulian Elischer 
842df8bae1dSRodney W. Grimes 	/*
843af38c68cSLuigi Rizzo 	 * Check for complete reassembly.
844df8bae1dSRodney W. Grimes 	 */
8456effc713SDoug Rabson 	next = 0;
8466effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
8476effc713SDoug Rabson 		if (GETIP(q)->ip_off != next)
8486effc713SDoug Rabson 			return (0);
8496effc713SDoug Rabson 		next += GETIP(q)->ip_len;
8506effc713SDoug Rabson 	}
8516effc713SDoug Rabson 	/* Make sure the last packet didn't have the IP_MF flag */
8526effc713SDoug Rabson 	if (p->m_flags & M_FRAG)
853df8bae1dSRodney W. Grimes 		return (0);
854df8bae1dSRodney W. Grimes 
855df8bae1dSRodney W. Grimes 	/*
856430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
857430d30d8SBill Fenner 	 */
8586effc713SDoug Rabson 	q = fp->ipq_frags;
8596effc713SDoug Rabson 	ip = GETIP(q);
8606effc713SDoug Rabson 	if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) {
861430d30d8SBill Fenner 		ipstat.ips_toolong++;
862430d30d8SBill Fenner 		ip_freef(fp);
863430d30d8SBill Fenner 		return (0);
864430d30d8SBill Fenner 	}
865430d30d8SBill Fenner 
866430d30d8SBill Fenner 	/*
867430d30d8SBill Fenner 	 * Concatenate fragments.
868df8bae1dSRodney W. Grimes 	 */
8696effc713SDoug Rabson 	m = q;
870df8bae1dSRodney W. Grimes 	t = m->m_next;
871df8bae1dSRodney W. Grimes 	m->m_next = 0;
872df8bae1dSRodney W. Grimes 	m_cat(m, t);
8736effc713SDoug Rabson 	nq = q->m_nextpkt;
874945aa40dSDoug Rabson 	q->m_nextpkt = 0;
8756effc713SDoug Rabson 	for (q = nq; q != NULL; q = nq) {
8766effc713SDoug Rabson 		nq = q->m_nextpkt;
877945aa40dSDoug Rabson 		q->m_nextpkt = NULL;
8786effc713SDoug Rabson 		m_cat(m, q);
879df8bae1dSRodney W. Grimes 	}
880df8bae1dSRodney W. Grimes 
88193e0e116SJulian Elischer #ifdef IPDIVERT
88293e0e116SJulian Elischer 	/*
883c977d4c7SJulian Elischer 	 * extract divert port for packet, if any
88493e0e116SJulian Elischer 	 */
88593e0e116SJulian Elischer 	frag_divert_port = fp->ipq_divert;
886c977d4c7SJulian Elischer 	ip_divert_cookie = fp->ipq_div_cookie;
88793e0e116SJulian Elischer #endif
88893e0e116SJulian Elischer 
889df8bae1dSRodney W. Grimes 	/*
890df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
891df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
892df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
893df8bae1dSRodney W. Grimes 	 * Make header visible.
894df8bae1dSRodney W. Grimes 	 */
895df8bae1dSRodney W. Grimes 	ip->ip_len = next;
8966effc713SDoug Rabson 	ip->ip_src = fp->ipq_src;
8976effc713SDoug Rabson 	ip->ip_dst = fp->ipq_dst;
898df8bae1dSRodney W. Grimes 	remque(fp);
899194a213eSAndrey A. Chernov 	nipq--;
900df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
9016effc713SDoug Rabson 	m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
9026effc713SDoug Rabson 	m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);
903df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
904df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
905df8bae1dSRodney W. Grimes 		register int plen = 0;
906df8bae1dSRodney W. Grimes 		for (t = m; m; m = m->m_next)
907df8bae1dSRodney W. Grimes 			plen += m->m_len;
908df8bae1dSRodney W. Grimes 		t->m_pkthdr.len = plen;
909df8bae1dSRodney W. Grimes 	}
9106effc713SDoug Rabson 	return (ip);
911df8bae1dSRodney W. Grimes 
912df8bae1dSRodney W. Grimes dropfrag:
913efe39c6aSJulian Elischer #ifdef IPDIVERT
914efe39c6aSJulian Elischer 	frag_divert_port = 0;
915efe39c6aSJulian Elischer 	ip_divert_cookie = 0;
916efe39c6aSJulian Elischer #endif
917df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
918df8bae1dSRodney W. Grimes 	m_freem(m);
919df8bae1dSRodney W. Grimes 	return (0);
9206effc713SDoug Rabson 
9216effc713SDoug Rabson #undef GETIP
922df8bae1dSRodney W. Grimes }
923df8bae1dSRodney W. Grimes 
924df8bae1dSRodney W. Grimes /*
925df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
926df8bae1dSRodney W. Grimes  * associated datagrams.
927df8bae1dSRodney W. Grimes  */
9280312fbe9SPoul-Henning Kamp static void
929df8bae1dSRodney W. Grimes ip_freef(fp)
930df8bae1dSRodney W. Grimes 	struct ipq *fp;
931df8bae1dSRodney W. Grimes {
9326effc713SDoug Rabson 	register struct mbuf *q;
933df8bae1dSRodney W. Grimes 
9346effc713SDoug Rabson 	while (fp->ipq_frags) {
9356effc713SDoug Rabson 		q = fp->ipq_frags;
9366effc713SDoug Rabson 		fp->ipq_frags = q->m_nextpkt;
9376effc713SDoug Rabson 		m_freem(q);
938df8bae1dSRodney W. Grimes 	}
939df8bae1dSRodney W. Grimes 	remque(fp);
940df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
941194a213eSAndrey A. Chernov 	nipq--;
942df8bae1dSRodney W. Grimes }
943df8bae1dSRodney W. Grimes 
944df8bae1dSRodney W. Grimes /*
945df8bae1dSRodney W. Grimes  * IP timer processing;
946df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
947df8bae1dSRodney W. Grimes  * queue, discard it.
948df8bae1dSRodney W. Grimes  */
949df8bae1dSRodney W. Grimes void
950df8bae1dSRodney W. Grimes ip_slowtimo()
951df8bae1dSRodney W. Grimes {
952df8bae1dSRodney W. Grimes 	register struct ipq *fp;
953df8bae1dSRodney W. Grimes 	int s = splnet();
954194a213eSAndrey A. Chernov 	int i;
955df8bae1dSRodney W. Grimes 
956194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
957194a213eSAndrey A. Chernov 		fp = ipq[i].next;
958194a213eSAndrey A. Chernov 		if (fp == 0)
959194a213eSAndrey A. Chernov 			continue;
960194a213eSAndrey A. Chernov 		while (fp != &ipq[i]) {
961df8bae1dSRodney W. Grimes 			--fp->ipq_ttl;
962df8bae1dSRodney W. Grimes 			fp = fp->next;
963df8bae1dSRodney W. Grimes 			if (fp->prev->ipq_ttl == 0) {
964df8bae1dSRodney W. Grimes 				ipstat.ips_fragtimeout++;
965df8bae1dSRodney W. Grimes 				ip_freef(fp->prev);
966df8bae1dSRodney W. Grimes 			}
967df8bae1dSRodney W. Grimes 		}
968194a213eSAndrey A. Chernov 	}
9691f91d8c5SDavid Greenman 	ipflow_slowtimo();
970df8bae1dSRodney W. Grimes 	splx(s);
971df8bae1dSRodney W. Grimes }
972df8bae1dSRodney W. Grimes 
973df8bae1dSRodney W. Grimes /*
974df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
975df8bae1dSRodney W. Grimes  */
976df8bae1dSRodney W. Grimes void
977df8bae1dSRodney W. Grimes ip_drain()
978df8bae1dSRodney W. Grimes {
979194a213eSAndrey A. Chernov 	int     i;
980ce29ab3aSGarrett Wollman 
981194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
982194a213eSAndrey A. Chernov 		while (ipq[i].next != &ipq[i]) {
983194a213eSAndrey A. Chernov 			ipstat.ips_fragdropped++;
984194a213eSAndrey A. Chernov 			ip_freef(ipq[i].next);
985194a213eSAndrey A. Chernov 		}
986194a213eSAndrey A. Chernov 	}
987ce29ab3aSGarrett Wollman 	in_rtqdrain();
988df8bae1dSRodney W. Grimes }
989df8bae1dSRodney W. Grimes 
990df8bae1dSRodney W. Grimes /*
991df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
992df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
993df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
994df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
995df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
996df8bae1dSRodney W. Grimes  */
9970312fbe9SPoul-Henning Kamp static int
998df8bae1dSRodney W. Grimes ip_dooptions(m)
999df8bae1dSRodney W. Grimes 	struct mbuf *m;
1000df8bae1dSRodney W. Grimes {
1001df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1002df8bae1dSRodney W. Grimes 	register u_char *cp;
1003df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
1004df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
1005df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
1006df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
1007df8bae1dSRodney W. Grimes 	n_time ntime;
1008df8bae1dSRodney W. Grimes 
1009df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
1010df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
101158938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1012df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
1013df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
1014df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
1015df8bae1dSRodney W. Grimes 			break;
1016df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
1017df8bae1dSRodney W. Grimes 			optlen = 1;
1018df8bae1dSRodney W. Grimes 		else {
1019df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
1020df8bae1dSRodney W. Grimes 			if (optlen <= 0 || optlen > cnt) {
1021df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1022df8bae1dSRodney W. Grimes 				goto bad;
1023df8bae1dSRodney W. Grimes 			}
1024df8bae1dSRodney W. Grimes 		}
1025df8bae1dSRodney W. Grimes 		switch (opt) {
1026df8bae1dSRodney W. Grimes 
1027df8bae1dSRodney W. Grimes 		default:
1028df8bae1dSRodney W. Grimes 			break;
1029df8bae1dSRodney W. Grimes 
1030df8bae1dSRodney W. Grimes 		/*
1031df8bae1dSRodney W. Grimes 		 * Source routing with record.
1032df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
1033df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
1034df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
1035df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
1036df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
1037df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
1038df8bae1dSRodney W. Grimes 		 */
1039df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
1040df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
1041df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1042df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1043df8bae1dSRodney W. Grimes 				goto bad;
1044df8bae1dSRodney W. Grimes 			}
1045df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
1046df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
1047df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
1048df8bae1dSRodney W. Grimes 			if (ia == 0) {
1049df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
1050df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
1051df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
1052df8bae1dSRodney W. Grimes 					goto bad;
1053df8bae1dSRodney W. Grimes 				}
1054bc189bf8SGuido van Rooij 				if (!ip_dosourceroute)
1055bc189bf8SGuido van Rooij 					goto nosourcerouting;
1056df8bae1dSRodney W. Grimes 				/*
1057df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
1058df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
1059df8bae1dSRodney W. Grimes 				 */
1060df8bae1dSRodney W. Grimes 				break;
1061df8bae1dSRodney W. Grimes 			}
1062df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1063df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr)) {
1064df8bae1dSRodney W. Grimes 				/*
1065df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
1066df8bae1dSRodney W. Grimes 				 */
10674fce5804SGuido van Rooij 				if (!ip_acceptsourceroute)
10684fce5804SGuido van Rooij 					goto nosourcerouting;
1069df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
1070df8bae1dSRodney W. Grimes 				break;
1071df8bae1dSRodney W. Grimes 			}
10721025071fSGarrett Wollman 
10731025071fSGarrett Wollman 			if (!ip_dosourceroute) {
10740af8d3ecSDavid Greenman 				if (ipforwarding) {
10750af8d3ecSDavid Greenman 					char buf[16]; /* aaa.bbb.ccc.ddd\0 */
10760af8d3ecSDavid Greenman 					/*
10770af8d3ecSDavid Greenman 					 * Acting as a router, so generate ICMP
10780af8d3ecSDavid Greenman 					 */
1079efa48587SGuido van Rooij nosourcerouting:
1080bc189bf8SGuido van Rooij 					strcpy(buf, inet_ntoa(ip->ip_dst));
10811025071fSGarrett Wollman 					log(LOG_WARNING,
10821025071fSGarrett Wollman 					    "attempted source route from %s to %s\n",
10831025071fSGarrett Wollman 					    inet_ntoa(ip->ip_src), buf);
10841025071fSGarrett Wollman 					type = ICMP_UNREACH;
10851025071fSGarrett Wollman 					code = ICMP_UNREACH_SRCFAIL;
10861025071fSGarrett Wollman 					goto bad;
10870af8d3ecSDavid Greenman 				} else {
10880af8d3ecSDavid Greenman 					/*
10890af8d3ecSDavid Greenman 					 * Not acting as a router, so silently drop.
10900af8d3ecSDavid Greenman 					 */
10910af8d3ecSDavid Greenman 					ipstat.ips_cantforward++;
10920af8d3ecSDavid Greenman 					m_freem(m);
10930af8d3ecSDavid Greenman 					return (1);
10940af8d3ecSDavid Greenman 				}
10951025071fSGarrett Wollman 			}
10961025071fSGarrett Wollman 
1097df8bae1dSRodney W. Grimes 			/*
1098df8bae1dSRodney W. Grimes 			 * locate outgoing interface
1099df8bae1dSRodney W. Grimes 			 */
110094a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
1101df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
11021025071fSGarrett Wollman 
1103df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
1104df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
1105df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
1106df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
1107df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
1108df8bae1dSRodney W. Grimes 			} else
1109df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
1110df8bae1dSRodney W. Grimes 			if (ia == 0) {
1111df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1112df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
1113df8bae1dSRodney W. Grimes 				goto bad;
1114df8bae1dSRodney W. Grimes 			}
1115df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
111694a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
111794a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1118df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1119df8bae1dSRodney W. Grimes 			/*
1120df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
1121df8bae1dSRodney W. Grimes 			 */
1122df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1123df8bae1dSRodney W. Grimes 			break;
1124df8bae1dSRodney W. Grimes 
1125df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1126df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1127df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1128df8bae1dSRodney W. Grimes 				goto bad;
1129df8bae1dSRodney W. Grimes 			}
1130df8bae1dSRodney W. Grimes 			/*
1131df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1132df8bae1dSRodney W. Grimes 			 */
1133df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1134df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr))
1135df8bae1dSRodney W. Grimes 				break;
113694a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1137df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1138df8bae1dSRodney W. Grimes 			/*
1139df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1140df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1141df8bae1dSRodney W. Grimes 			 */
1142df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
1143df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
1144df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1145df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1146df8bae1dSRodney W. Grimes 				goto bad;
1147df8bae1dSRodney W. Grimes 			}
114894a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
114994a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1150df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1151df8bae1dSRodney W. Grimes 			break;
1152df8bae1dSRodney W. Grimes 
1153df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1154df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
1155df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
1156df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
1157df8bae1dSRodney W. Grimes 				goto bad;
11580c8d2590SBruce Evans 			if (ipt->ipt_ptr > ipt->ipt_len - sizeof(int32_t)) {
1159df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
1160df8bae1dSRodney W. Grimes 					goto bad;
1161df8bae1dSRodney W. Grimes 				break;
1162df8bae1dSRodney W. Grimes 			}
1163df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
1164df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
1165df8bae1dSRodney W. Grimes 
1166df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1167df8bae1dSRodney W. Grimes 				break;
1168df8bae1dSRodney W. Grimes 
1169df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
1170b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1171df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1172df8bae1dSRodney W. Grimes 					goto bad;
1173df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1174df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1175df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
1176df8bae1dSRodney W. Grimes 				if (ia == 0)
1177df8bae1dSRodney W. Grimes 					continue;
117894a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
117994a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
1180df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1181df8bae1dSRodney W. Grimes 				break;
1182df8bae1dSRodney W. Grimes 
1183df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1184b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1185df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1186df8bae1dSRodney W. Grimes 					goto bad;
118794a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1188df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1189df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1190df8bae1dSRodney W. Grimes 					continue;
1191df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1192df8bae1dSRodney W. Grimes 				break;
1193df8bae1dSRodney W. Grimes 
1194df8bae1dSRodney W. Grimes 			default:
1195df8bae1dSRodney W. Grimes 				goto bad;
1196df8bae1dSRodney W. Grimes 			}
1197df8bae1dSRodney W. Grimes 			ntime = iptime();
119894a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1199df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1200df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1201df8bae1dSRodney W. Grimes 		}
1202df8bae1dSRodney W. Grimes 	}
120347174b49SAndrey A. Chernov 	if (forward && ipforwarding) {
1204df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1205df8bae1dSRodney W. Grimes 		return (1);
1206df8bae1dSRodney W. Grimes 	}
1207df8bae1dSRodney W. Grimes 	return (0);
1208df8bae1dSRodney W. Grimes bad:
120958938916SGarrett Wollman 	ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2;   /* XXX icmp_error adds in hdr length */
1210df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1211df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1212df8bae1dSRodney W. Grimes 	return (1);
1213df8bae1dSRodney W. Grimes }
1214df8bae1dSRodney W. Grimes 
1215df8bae1dSRodney W. Grimes /*
1216df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1217df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1218df8bae1dSRodney W. Grimes  */
12190312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1220df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1221df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1222df8bae1dSRodney W. Grimes {
1223df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1224df8bae1dSRodney W. Grimes 
1225df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1226df8bae1dSRodney W. Grimes 
1227df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1228df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1229df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1230df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1231df8bae1dSRodney W. Grimes 		}
1232df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1233df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1234df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1235df8bae1dSRodney W. Grimes 
12362c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1237df8bae1dSRodney W. Grimes 	}
1238df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1239df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1240df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1241df8bae1dSRodney W. Grimes }
1242df8bae1dSRodney W. Grimes 
1243df8bae1dSRodney W. Grimes /*
1244df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1245df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1246df8bae1dSRodney W. Grimes  */
1247df8bae1dSRodney W. Grimes void
1248df8bae1dSRodney W. Grimes save_rte(option, dst)
1249df8bae1dSRodney W. Grimes 	u_char *option;
1250df8bae1dSRodney W. Grimes 	struct in_addr dst;
1251df8bae1dSRodney W. Grimes {
1252df8bae1dSRodney W. Grimes 	unsigned olen;
1253df8bae1dSRodney W. Grimes 
1254df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1255df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1256df8bae1dSRodney W. Grimes 	if (ipprintfs)
1257df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1258df8bae1dSRodney W. Grimes #endif
1259df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1260df8bae1dSRodney W. Grimes 		return;
12610453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1262df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1263df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1264df8bae1dSRodney W. Grimes }
1265df8bae1dSRodney W. Grimes 
1266df8bae1dSRodney W. Grimes /*
1267df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1268df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1269df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1270df8bae1dSRodney W. Grimes  */
1271df8bae1dSRodney W. Grimes struct mbuf *
1272df8bae1dSRodney W. Grimes ip_srcroute()
1273df8bae1dSRodney W. Grimes {
1274df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1275df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1276df8bae1dSRodney W. Grimes 
1277df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1278df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1279cfe8b629SGarrett Wollman 	m = m_get(M_DONTWAIT, MT_HEADER);
1280df8bae1dSRodney W. Grimes 	if (m == 0)
1281df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1282df8bae1dSRodney W. Grimes 
1283df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1284df8bae1dSRodney W. Grimes 
1285df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1286df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1287df8bae1dSRodney W. Grimes 	    OPTSIZ;
1288df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1289df8bae1dSRodney W. Grimes 	if (ipprintfs)
1290df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1291df8bae1dSRodney W. Grimes #endif
1292df8bae1dSRodney W. Grimes 
1293df8bae1dSRodney W. Grimes 	/*
1294df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1295df8bae1dSRodney W. Grimes 	 */
1296df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1297df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1298df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1299df8bae1dSRodney W. Grimes 	if (ipprintfs)
1300af38c68cSLuigi Rizzo 		printf(" hops %lx", (u_long)ntohl(mtod(m, struct in_addr *)->s_addr));
1301df8bae1dSRodney W. Grimes #endif
1302df8bae1dSRodney W. Grimes 
1303df8bae1dSRodney W. Grimes 	/*
1304df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1305df8bae1dSRodney W. Grimes 	 */
1306df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1307df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
130894a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
130994a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1310df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1311df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1312df8bae1dSRodney W. Grimes #undef OPTSIZ
1313df8bae1dSRodney W. Grimes 	/*
1314df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1315df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1316df8bae1dSRodney W. Grimes 	 */
1317df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1318df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1319df8bae1dSRodney W. Grimes 		if (ipprintfs)
1320af38c68cSLuigi Rizzo 			printf(" %lx", (u_long)ntohl(q->s_addr));
1321df8bae1dSRodney W. Grimes #endif
1322df8bae1dSRodney W. Grimes 		*q++ = *p--;
1323df8bae1dSRodney W. Grimes 	}
1324df8bae1dSRodney W. Grimes 	/*
1325df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1326df8bae1dSRodney W. Grimes 	 */
1327df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1328df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1329df8bae1dSRodney W. Grimes 	if (ipprintfs)
1330af38c68cSLuigi Rizzo 		printf(" %lx\n", (u_long)ntohl(q->s_addr));
1331df8bae1dSRodney W. Grimes #endif
1332df8bae1dSRodney W. Grimes 	return (m);
1333df8bae1dSRodney W. Grimes }
1334df8bae1dSRodney W. Grimes 
1335df8bae1dSRodney W. Grimes /*
1336df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1337df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1338df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1339df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1340df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1341df8bae1dSRodney W. Grimes  */
1342df8bae1dSRodney W. Grimes void
1343df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1344df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1345df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1346df8bae1dSRodney W. Grimes {
1347df8bae1dSRodney W. Grimes 	register int i;
1348df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1349df8bae1dSRodney W. Grimes 	register caddr_t opts;
1350df8bae1dSRodney W. Grimes 	int olen;
1351df8bae1dSRodney W. Grimes 
135258938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1353df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1354df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1355df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1356df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1357df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1358df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
135958938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1360df8bae1dSRodney W. Grimes }
1361df8bae1dSRodney W. Grimes 
1362df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1363df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1364df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1365df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1366df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1367df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1368df8bae1dSRodney W. Grimes 	ENOPROTOOPT
1369df8bae1dSRodney W. Grimes };
1370df8bae1dSRodney W. Grimes 
1371df8bae1dSRodney W. Grimes /*
1372df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1373df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1374df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1375df8bae1dSRodney W. Grimes  * of codes and types.
1376df8bae1dSRodney W. Grimes  *
1377df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1378df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1379df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1380df8bae1dSRodney W. Grimes  * protocol deal with that.
1381df8bae1dSRodney W. Grimes  *
1382df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1383df8bae1dSRodney W. Grimes  * via a source route.
1384df8bae1dSRodney W. Grimes  */
13850312fbe9SPoul-Henning Kamp static void
1386df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1387df8bae1dSRodney W. Grimes 	struct mbuf *m;
1388df8bae1dSRodney W. Grimes 	int srcrt;
1389df8bae1dSRodney W. Grimes {
1390df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1391df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1392df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
139326f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1394df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1395df8bae1dSRodney W. Grimes 	n_long dest;
1396df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
1397df8bae1dSRodney W. Grimes 
1398df8bae1dSRodney W. Grimes 	dest = 0;
1399df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1400df8bae1dSRodney W. Grimes 	if (ipprintfs)
140161ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1402162886e2SBruce Evans 		    (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr,
1403162886e2SBruce Evans 		    ip->ip_ttl);
1404df8bae1dSRodney W. Grimes #endif
1405100ba1a6SJordan K. Hubbard 
1406100ba1a6SJordan K. Hubbard 
140792af003dSGarrett Wollman 	if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) {
1408df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1409df8bae1dSRodney W. Grimes 		m_freem(m);
1410df8bae1dSRodney W. Grimes 		return;
1411df8bae1dSRodney W. Grimes 	}
1412df8bae1dSRodney W. Grimes 	HTONS(ip->ip_id);
14131b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
14141b968362SDag-Erling Smørgrav 	if (!ipstealth) {
14151b968362SDag-Erling Smørgrav #endif
1416df8bae1dSRodney W. Grimes 		if (ip->ip_ttl <= IPTTLDEC) {
14171b968362SDag-Erling Smørgrav 			icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
14181b968362SDag-Erling Smørgrav 			    dest, 0);
1419df8bae1dSRodney W. Grimes 			return;
1420df8bae1dSRodney W. Grimes 		}
1421df8bae1dSRodney W. Grimes 		ip->ip_ttl -= IPTTLDEC;
14221b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
14231b968362SDag-Erling Smørgrav 	}
14241b968362SDag-Erling Smørgrav #endif
1425df8bae1dSRodney W. Grimes 
1426df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1427df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1428df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1429df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1430df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1431df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1432df8bae1dSRodney W. Grimes 		}
1433df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1434df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1435df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1436df8bae1dSRodney W. Grimes 
14372c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1438df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1439df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1440df8bae1dSRodney W. Grimes 			return;
1441df8bae1dSRodney W. Grimes 		}
1442df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1443df8bae1dSRodney W. Grimes 	}
1444df8bae1dSRodney W. Grimes 
1445df8bae1dSRodney W. Grimes 	/*
1446df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1447df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1448df8bae1dSRodney W. Grimes 	 */
1449df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
1450df8bae1dSRodney W. Grimes 
1451df8bae1dSRodney W. Grimes 	/*
1452df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1453df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1454df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1455df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1456df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1457df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1458df8bae1dSRodney W. Grimes 	 */
1459df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1460df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1461df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1462df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1463df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1464df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1465df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1466df8bae1dSRodney W. Grimes 
1467df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1468df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1469df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1470df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1471df8bae1dSRodney W. Grimes 		    else
1472df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1473df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1474df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1475df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1476df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1477df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1478df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1479df8bae1dSRodney W. Grimes #endif
1480df8bae1dSRodney W. Grimes 		}
1481df8bae1dSRodney W. Grimes 	}
1482df8bae1dSRodney W. Grimes 
1483b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1484b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1485df8bae1dSRodney W. Grimes 	if (error)
1486df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1487df8bae1dSRodney W. Grimes 	else {
1488df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1489df8bae1dSRodney W. Grimes 		if (type)
1490df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1491df8bae1dSRodney W. Grimes 		else {
14921f91d8c5SDavid Greenman 			if (mcopy) {
14931f91d8c5SDavid Greenman 				ipflow_create(&ipforward_rt, mcopy);
1494df8bae1dSRodney W. Grimes 				m_freem(mcopy);
14951f91d8c5SDavid Greenman 			}
1496df8bae1dSRodney W. Grimes 			return;
1497df8bae1dSRodney W. Grimes 		}
1498df8bae1dSRodney W. Grimes 	}
1499df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1500df8bae1dSRodney W. Grimes 		return;
1501df8bae1dSRodney W. Grimes 	destifp = NULL;
1502df8bae1dSRodney W. Grimes 
1503df8bae1dSRodney W. Grimes 	switch (error) {
1504df8bae1dSRodney W. Grimes 
1505df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1506df8bae1dSRodney W. Grimes 		/* type, code set above */
1507df8bae1dSRodney W. Grimes 		break;
1508df8bae1dSRodney W. Grimes 
1509df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1510df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1511df8bae1dSRodney W. Grimes 	case ENETDOWN:
1512df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1513df8bae1dSRodney W. Grimes 	default:
1514df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1515df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1516df8bae1dSRodney W. Grimes 		break;
1517df8bae1dSRodney W. Grimes 
1518df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1519df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1520df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
1521df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1522df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
1523df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1524df8bae1dSRodney W. Grimes 		break;
1525df8bae1dSRodney W. Grimes 
1526df8bae1dSRodney W. Grimes 	case ENOBUFS:
1527df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1528df8bae1dSRodney W. Grimes 		code = 0;
1529df8bae1dSRodney W. Grimes 		break;
1530df8bae1dSRodney W. Grimes 	}
1531df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1532df8bae1dSRodney W. Grimes }
1533df8bae1dSRodney W. Grimes 
153482c23ebaSBill Fenner void
153582c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
153682c23ebaSBill Fenner 	register struct inpcb *inp;
153782c23ebaSBill Fenner 	register struct mbuf **mp;
153882c23ebaSBill Fenner 	register struct ip *ip;
153982c23ebaSBill Fenner 	register struct mbuf *m;
154082c23ebaSBill Fenner {
154182c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
154282c23ebaSBill Fenner 		struct timeval tv;
154382c23ebaSBill Fenner 
154482c23ebaSBill Fenner 		microtime(&tv);
154582c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
154682c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
154782c23ebaSBill Fenner 		if (*mp)
154882c23ebaSBill Fenner 			mp = &(*mp)->m_next;
154982c23ebaSBill Fenner 	}
155082c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
155182c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
155282c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
155382c23ebaSBill Fenner 		if (*mp)
155482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
155582c23ebaSBill Fenner 	}
155682c23ebaSBill Fenner #ifdef notyet
155782c23ebaSBill Fenner 	/* XXX
155882c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
155982c23ebaSBill Fenner 	 * than they already were.
156082c23ebaSBill Fenner 	 */
156182c23ebaSBill Fenner 	/* options were tossed already */
156282c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
156382c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
156482c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
156582c23ebaSBill Fenner 		if (*mp)
156682c23ebaSBill Fenner 			mp = &(*mp)->m_next;
156782c23ebaSBill Fenner 	}
156882c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
156982c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
157082c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
157182c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
157282c23ebaSBill Fenner 		if (*mp)
157382c23ebaSBill Fenner 			mp = &(*mp)->m_next;
157482c23ebaSBill Fenner 	}
157582c23ebaSBill Fenner #endif
157682c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
1577d314ad7bSJulian Elischer 		struct ifnet *ifp;
1578d314ad7bSJulian Elischer 		struct sdlbuf {
157982c23ebaSBill Fenner 			struct sockaddr_dl sdl;
1580d314ad7bSJulian Elischer 			u_char	pad[32];
1581d314ad7bSJulian Elischer 		} sdlbuf;
1582d314ad7bSJulian Elischer 		struct sockaddr_dl *sdp;
1583d314ad7bSJulian Elischer 		struct sockaddr_dl *sdl2 = &sdlbuf.sdl;
158482c23ebaSBill Fenner 
1585d314ad7bSJulian Elischer 		if (((ifp = m->m_pkthdr.rcvif))
1586d314ad7bSJulian Elischer 		&& ( ifp->if_index && (ifp->if_index <= if_index))) {
1587d314ad7bSJulian Elischer 			sdp = (struct sockaddr_dl *)(ifnet_addrs
1588d314ad7bSJulian Elischer 					[ifp->if_index - 1]->ifa_addr);
1589d314ad7bSJulian Elischer 			/*
1590d314ad7bSJulian Elischer 			 * Change our mind and don't try copy.
1591d314ad7bSJulian Elischer 			 */
1592d314ad7bSJulian Elischer 			if ((sdp->sdl_family != AF_LINK)
1593d314ad7bSJulian Elischer 			|| (sdp->sdl_len > sizeof(sdlbuf))) {
1594d314ad7bSJulian Elischer 				goto makedummy;
1595d314ad7bSJulian Elischer 			}
1596d314ad7bSJulian Elischer 			bcopy(sdp, sdl2, sdp->sdl_len);
1597d314ad7bSJulian Elischer 		} else {
1598d314ad7bSJulian Elischer makedummy:
1599d314ad7bSJulian Elischer 			sdl2->sdl_len
1600d314ad7bSJulian Elischer 				= offsetof(struct sockaddr_dl, sdl_data[0]);
1601d314ad7bSJulian Elischer 			sdl2->sdl_family = AF_LINK;
1602d314ad7bSJulian Elischer 			sdl2->sdl_index = 0;
1603d314ad7bSJulian Elischer 			sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0;
1604d314ad7bSJulian Elischer 		}
1605d314ad7bSJulian Elischer 		*mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len,
160682c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
160782c23ebaSBill Fenner 		if (*mp)
160882c23ebaSBill Fenner 			mp = &(*mp)->m_next;
160982c23ebaSBill Fenner 	}
161082c23ebaSBill Fenner }
161182c23ebaSBill Fenner 
1612df8bae1dSRodney W. Grimes int
1613f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1614f0068c4aSGarrett Wollman {
1615f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1616f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1617f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1618f0068c4aSGarrett Wollman 
1619f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1620f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1621f0068c4aSGarrett Wollman 
1622f0068c4aSGarrett Wollman 	ip_rsvpd = so;
16231c5de19aSGarrett Wollman 	/*
16241c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
16251c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
16261c5de19aSGarrett Wollman 	 */
16271c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
16281c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
16291c5de19aSGarrett Wollman 		rsvp_on++;
16301c5de19aSGarrett Wollman 	}
1631f0068c4aSGarrett Wollman 
1632f0068c4aSGarrett Wollman 	return 0;
1633f0068c4aSGarrett Wollman }
1634f0068c4aSGarrett Wollman 
1635f0068c4aSGarrett Wollman int
1636f0068c4aSGarrett Wollman ip_rsvp_done(void)
1637f0068c4aSGarrett Wollman {
1638f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
16391c5de19aSGarrett Wollman 	/*
16401c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
16411c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
16421c5de19aSGarrett Wollman 	 */
16431c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
16441c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
16451c5de19aSGarrett Wollman 		rsvp_on--;
16461c5de19aSGarrett Wollman 	}
1647f0068c4aSGarrett Wollman 	return 0;
1648f0068c4aSGarrett Wollman }
1649