xref: /freebsd/sys/netinet/ip_input.c (revision b715f178c6489ac99e3c10fb85bb6b42146648b6)
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
34b715f178SLuigi Rizzo  * $Id: ip_input.c,v 1.101 1998/09/10 08:56:40 dfr 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"
42b715f178SLuigi Rizzo #include "opt_ipdn.h"
43fbd1372aSJoerg Wunsch #include "opt_ipdivert.h"
441ee25934SPeter Wemm #include "opt_ipfilter.h"
4574a9466cSGary Palmer 
4682c23ebaSBill Fenner #include <stddef.h>
4782c23ebaSBill Fenner 
48df8bae1dSRodney W. Grimes #include <sys/param.h>
49df8bae1dSRodney W. Grimes #include <sys/systm.h>
50df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
51b715f178SLuigi Rizzo #include <sys/malloc.h>
52df8bae1dSRodney W. Grimes #include <sys/domain.h>
53df8bae1dSRodney W. Grimes #include <sys/protosw.h>
54df8bae1dSRodney W. Grimes #include <sys/socket.h>
55df8bae1dSRodney W. Grimes #include <sys/time.h>
56df8bae1dSRodney W. Grimes #include <sys/kernel.h>
571025071fSGarrett Wollman #include <sys/syslog.h>
58b5e8ce9fSBruce Evans #include <sys/sysctl.h>
59df8bae1dSRodney W. Grimes 
60df8bae1dSRodney W. Grimes #include <net/if.h>
61d314ad7bSJulian Elischer #include <net/if_var.h>
6282c23ebaSBill Fenner #include <net/if_dl.h>
63df8bae1dSRodney W. Grimes #include <net/route.h>
64748e0b0aSGarrett Wollman #include <net/netisr.h>
65df8bae1dSRodney W. Grimes 
66df8bae1dSRodney W. Grimes #include <netinet/in.h>
67df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
68b5e8ce9fSBruce Evans #include <netinet/in_var.h>
69df8bae1dSRodney W. Grimes #include <netinet/ip.h>
70df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
71df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
72df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
7358938916SGarrett Wollman #include <machine/in_cksum.h>
74df8bae1dSRodney W. Grimes 
75f0068c4aSGarrett Wollman #include <sys/socketvar.h>
766ddbf1e2SGary Palmer 
776ddbf1e2SGary Palmer #ifdef IPFIREWALL
786ddbf1e2SGary Palmer #include <netinet/ip_fw.h>
796ddbf1e2SGary Palmer #endif
806ddbf1e2SGary Palmer 
81b715f178SLuigi Rizzo #ifdef DUMMYNET
82b715f178SLuigi Rizzo #include <netinet/ip_dummynet.h>
83b715f178SLuigi Rizzo #endif
84b715f178SLuigi Rizzo 
851c5de19aSGarrett Wollman int rsvp_on = 0;
86f708ef1bSPoul-Henning Kamp static int ip_rsvp_on;
87f0068c4aSGarrett Wollman struct socket *ip_rsvpd;
88f0068c4aSGarrett Wollman 
891f91d8c5SDavid Greenman int	ipforwarding = 0;
900312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW,
910312fbe9SPoul-Henning Kamp 	&ipforwarding, 0, "");
920312fbe9SPoul-Henning Kamp 
93d4fb926cSGarrett Wollman static int	ipsendredirects = 1; /* XXX */
940312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW,
950312fbe9SPoul-Henning Kamp 	&ipsendredirects, 0, "");
960312fbe9SPoul-Henning Kamp 
97df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
980312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW,
990312fbe9SPoul-Henning Kamp 	&ip_defttl, 0, "");
1000312fbe9SPoul-Henning Kamp 
1010312fbe9SPoul-Henning Kamp static int	ip_dosourceroute = 0;
1020312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
1030312fbe9SPoul-Henning Kamp 	&ip_dosourceroute, 0, "");
1044fce5804SGuido van Rooij 
1054fce5804SGuido van Rooij static int	ip_acceptsourceroute = 0;
1064fce5804SGuido van Rooij SYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute,
1074fce5804SGuido van Rooij 	CTLFLAG_RW, &ip_acceptsourceroute, 0, "");
108df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1090312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
110df8bae1dSRodney W. Grimes #endif
111df8bae1dSRodney W. Grimes 
112df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
113df8bae1dSRodney W. Grimes extern	struct protosw inetsw[];
114df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
1150312fbe9SPoul-Henning Kamp static int	ipqmaxlen = IFQ_MAXLEN;
11659562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; /* first inet address */
117df8bae1dSRodney W. Grimes struct	ifqueue ipintrq;
1180312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RD,
1190312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_maxlen, 0, "");
1200312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1210312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_drops, 0, "");
122df8bae1dSRodney W. Grimes 
123f23b4c91SGarrett Wollman struct ipstat ipstat;
1246fce01c9SGarrett Wollman SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RD,
1256fce01c9SGarrett Wollman 	&ipstat, ipstat, "");
126194a213eSAndrey A. Chernov 
127194a213eSAndrey A. Chernov /* Packet reassembly stuff */
128194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
129194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
130194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
131194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
132194a213eSAndrey A. Chernov 	((((x) & 0xF | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
133194a213eSAndrey A. Chernov 
134194a213eSAndrey A. Chernov static struct ipq ipq[IPREASS_NHASH];
135194a213eSAndrey A. Chernov static int    nipq = 0;         /* total # of reass queues */
136194a213eSAndrey A. Chernov static int    maxnipq;
137f23b4c91SGarrett Wollman 
1380312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1390312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1400312fbe9SPoul-Henning Kamp 	&ip_mtu, 0, "");
1410312fbe9SPoul-Henning Kamp #endif
1420312fbe9SPoul-Henning Kamp 
14358938916SGarrett Wollman #if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1
14458938916SGarrett Wollman #undef COMPAT_IPFW
14558938916SGarrett Wollman #define COMPAT_IPFW 1
14658938916SGarrett Wollman #else
14758938916SGarrett Wollman #undef COMPAT_IPFW
14858938916SGarrett Wollman #endif
14958938916SGarrett Wollman 
15058938916SGarrett Wollman #ifdef COMPAT_IPFW
151cfe8b629SGarrett Wollman 
152cfe8b629SGarrett Wollman #include <netinet/ip_fw.h>
153cfe8b629SGarrett Wollman 
15423bf9953SPoul-Henning Kamp /* Firewall hooks */
15523bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
15623bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
157e7319babSPoul-Henning Kamp 
158b715f178SLuigi Rizzo #ifdef DUMMYNET
159b715f178SLuigi Rizzo ip_dn_ctl_t *ip_dn_ctl_ptr;
160b715f178SLuigi Rizzo #endif
161b715f178SLuigi Rizzo 
162fed1c7e9SSøren Schmidt /* IP Network Address Translation (NAT) hooks */
163fed1c7e9SSøren Schmidt ip_nat_t *ip_nat_ptr;
164fed1c7e9SSøren Schmidt ip_nat_ctl_t *ip_nat_ctl_ptr;
16558938916SGarrett Wollman #endif
166fed1c7e9SSøren Schmidt 
167afed1b49SDarren Reed #if defined(IPFILTER_LKM) || defined(IPFILTER)
1681ee25934SPeter Wemm int iplattach __P((void));
169afed1b49SDarren Reed int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)) = NULL;
170afed1b49SDarren Reed #endif
171afed1b49SDarren Reed 
172afed1b49SDarren Reed 
173e7319babSPoul-Henning Kamp /*
174df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
175df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
176df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
177df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
178df8bae1dSRodney W. Grimes  * to us.
179df8bae1dSRodney W. Grimes  */
1800312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
181df8bae1dSRodney W. Grimes static	struct ip_srcrt {
182df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
183df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
184df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
185df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
186df8bae1dSRodney W. Grimes } ip_srcrt;
187df8bae1dSRodney W. Grimes 
18893e0e116SJulian Elischer #ifdef IPDIVERT
18993e0e116SJulian Elischer /*
19093e0e116SJulian Elischer  * Shared variable between ip_input() and ip_reass() to communicate
19193e0e116SJulian Elischer  * about which packets, once assembled from fragments, get diverted,
19293e0e116SJulian Elischer  * and to which port.
19393e0e116SJulian Elischer  */
19493e0e116SJulian Elischer static u_short	frag_divert_port;
19593e0e116SJulian Elischer #endif
19693e0e116SJulian Elischer 
197f9e354dfSJulian Elischer struct sockaddr_in *ip_fw_fwd_addr;
198f9e354dfSJulian Elischer 
199df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr));
2000312fbe9SPoul-Henning Kamp static int	 ip_dooptions __P((struct mbuf *));
2010312fbe9SPoul-Henning Kamp static void	 ip_forward __P((struct mbuf *, int));
2020312fbe9SPoul-Henning Kamp static void	 ip_freef __P((struct ipq *));
2030312fbe9SPoul-Henning Kamp static struct ip *
2046effc713SDoug Rabson 	 ip_reass __P((struct mbuf *, struct ipq *, struct ipq *));
2050312fbe9SPoul-Henning Kamp static struct in_ifaddr *
2060312fbe9SPoul-Henning Kamp 	 ip_rtaddr __P((struct in_addr));
2070312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
208df8bae1dSRodney W. Grimes /*
209df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
210df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
211df8bae1dSRodney W. Grimes  */
212df8bae1dSRodney W. Grimes void
213df8bae1dSRodney W. Grimes ip_init()
214df8bae1dSRodney W. Grimes {
215df8bae1dSRodney W. Grimes 	register struct protosw *pr;
216df8bae1dSRodney W. Grimes 	register int i;
217df8bae1dSRodney W. Grimes 
21859562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
219df8bae1dSRodney W. Grimes 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
220df8bae1dSRodney W. Grimes 	if (pr == 0)
221df8bae1dSRodney W. Grimes 		panic("ip_init");
222df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
223df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
224df8bae1dSRodney W. Grimes 	for (pr = inetdomain.dom_protosw;
225df8bae1dSRodney W. Grimes 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
226df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
227df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
228df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
229194a213eSAndrey A. Chernov 
230194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
231194a213eSAndrey A. Chernov 	    ipq[i].next = ipq[i].prev = &ipq[i];
232194a213eSAndrey A. Chernov 
233194a213eSAndrey A. Chernov 	maxnipq = nmbclusters/4;
234194a213eSAndrey A. Chernov 
235227ee8a1SPoul-Henning Kamp 	ip_id = time_second & 0xffff;
236df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
237b715f178SLuigi Rizzo #ifdef IPFIREWALL
238b715f178SLuigi Rizzo 	ip_fw_init();
239b715f178SLuigi Rizzo #endif
240b715f178SLuigi Rizzo #ifdef DUMMYNET
241b715f178SLuigi Rizzo 	ip_dn_init();
242b715f178SLuigi Rizzo #endif
243fed1c7e9SSøren Schmidt #ifdef IPNAT
244fed1c7e9SSøren Schmidt         ip_nat_init();
245fed1c7e9SSøren Schmidt #endif
2461ee25934SPeter Wemm #ifdef IPFILTER
2471ee25934SPeter Wemm         iplattach();
2481ee25934SPeter Wemm #endif
249fed1c7e9SSøren Schmidt 
250df8bae1dSRodney W. Grimes }
251df8bae1dSRodney W. Grimes 
2520312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
253f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
254df8bae1dSRodney W. Grimes 
255df8bae1dSRodney W. Grimes /*
256df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
257df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
258df8bae1dSRodney W. Grimes  */
259c67b1d17SGarrett Wollman void
260c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
261df8bae1dSRodney W. Grimes {
26223bf9953SPoul-Henning Kamp 	struct ip *ip;
26323bf9953SPoul-Henning Kamp 	struct ipq *fp;
264b715f178SLuigi Rizzo 	struct ipqent *ipqe;
26523bf9953SPoul-Henning Kamp 	struct in_ifaddr *ia;
2666effc713SDoug Rabson 	int    i, hlen, mff;
26747c861ecSBrian Somers 	u_short sum;
268b715f178SLuigi Rizzo #ifndef IPDIVERT /* dummy variable for the firewall code to play with */
269b715f178SLuigi Rizzo         u_short ip_divert_cookie = 0 ;
270b715f178SLuigi Rizzo #endif
271b715f178SLuigi Rizzo #ifdef COMPAT_IPFW
272b715f178SLuigi Rizzo 	struct ip_fw_chain *rule = NULL ;
273b715f178SLuigi Rizzo #endif
274b715f178SLuigi Rizzo 
275b715f178SLuigi Rizzo #if defined(IPFIREWALL) && defined(DUMMYNET)
276b715f178SLuigi Rizzo         /*
277b715f178SLuigi Rizzo          * dummynet packet are prepended a vestigial mbuf with
278b715f178SLuigi Rizzo          * m_type = MT_DUMMYNET and m_data pointing to the matching
279b715f178SLuigi Rizzo          * rule.
280b715f178SLuigi Rizzo          */
281b715f178SLuigi Rizzo         if (m->m_type == MT_DUMMYNET) {
282b715f178SLuigi Rizzo             struct mbuf *m0 = m ;
283b715f178SLuigi Rizzo             rule = (struct ip_fw_chain *)(m->m_data) ;
284b715f178SLuigi Rizzo             m = m->m_next ;
285b715f178SLuigi Rizzo             free(m0, M_IPFW);
286b715f178SLuigi Rizzo             ip = mtod(m, struct ip *);
287b715f178SLuigi Rizzo             hlen = IP_VHL_HL(ip->ip_vhl) << 2;
288b715f178SLuigi Rizzo             goto iphack ;
289b715f178SLuigi Rizzo         } else
290b715f178SLuigi Rizzo             rule = NULL ;
291b715f178SLuigi Rizzo #endif
292df8bae1dSRodney W. Grimes 
293df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
294ed7509acSJulian Elischer 	if (m == NULL || (m->m_flags & M_PKTHDR) == 0)
29558938916SGarrett Wollman 		panic("ip_input no HDR");
296df8bae1dSRodney W. Grimes #endif
297df8bae1dSRodney W. Grimes 	/*
298df8bae1dSRodney W. Grimes 	 * If no IP addresses have been set yet but the interfaces
299df8bae1dSRodney W. Grimes 	 * are receiving, can't do anything with incoming packets yet.
30059562606SGarrett Wollman 	 * XXX This is broken! We should be able to receive broadcasts
30159562606SGarrett Wollman 	 * and multicasts even without any local addresses configured.
302df8bae1dSRodney W. Grimes 	 */
30359562606SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead))
304df8bae1dSRodney W. Grimes 		goto bad;
305df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
30658938916SGarrett Wollman 
30758938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
30858938916SGarrett Wollman 		goto tooshort;
30958938916SGarrett Wollman 
310df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
311df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
312df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
313c67b1d17SGarrett Wollman 		return;
314df8bae1dSRodney W. Grimes 	}
315df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
31658938916SGarrett Wollman 
31758938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
318df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
319df8bae1dSRodney W. Grimes 		goto bad;
320df8bae1dSRodney W. Grimes 	}
32158938916SGarrett Wollman 
32258938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
323df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
324df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
325df8bae1dSRodney W. Grimes 		goto bad;
326df8bae1dSRodney W. Grimes 	}
327df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
328df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
329df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
330c67b1d17SGarrett Wollman 			return;
331df8bae1dSRodney W. Grimes 		}
332df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
333df8bae1dSRodney W. Grimes 	}
33458938916SGarrett Wollman 	if (hlen == sizeof(struct ip)) {
33547c861ecSBrian Somers 		sum = in_cksum_hdr(ip);
33658938916SGarrett Wollman 	} else {
33747c861ecSBrian Somers 		sum = in_cksum(m, hlen);
33858938916SGarrett Wollman 	}
33947c861ecSBrian Somers 	if (sum) {
340df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
341df8bae1dSRodney W. Grimes 		goto bad;
342df8bae1dSRodney W. Grimes 	}
343df8bae1dSRodney W. Grimes 
344df8bae1dSRodney W. Grimes 	/*
345df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
346df8bae1dSRodney W. Grimes 	 */
347df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
348df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
349df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
350df8bae1dSRodney W. Grimes 		goto bad;
351df8bae1dSRodney W. Grimes 	}
352df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_id);
353df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
354df8bae1dSRodney W. Grimes 
355df8bae1dSRodney W. Grimes 	/*
356df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
357df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
358df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
359df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
360df8bae1dSRodney W. Grimes 	 */
361df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
36258938916SGarrett Wollman tooshort:
363df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
364df8bae1dSRodney W. Grimes 		goto bad;
365df8bae1dSRodney W. Grimes 	}
366df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
367df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
368df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
369df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
370df8bae1dSRodney W. Grimes 		} else
371df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
372df8bae1dSRodney W. Grimes 	}
3734dd1662bSUgen J.S. Antsilevich 	/*
3744dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3754dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
3764dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
3774dd1662bSUgen J.S. Antsilevich 	 * deals with it.
37893e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
379fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
380b715f178SLuigi Rizzo 	 * - Pipe: pass pkt through dummynet.
3814dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
3824dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
3834dd1662bSUgen J.S. Antsilevich  	 */
384b715f178SLuigi Rizzo 
385b715f178SLuigi Rizzo iphack:
386beec8214SDarren Reed #if defined(IPFILTER) || defined(IPFILTER_LKM)
387beec8214SDarren Reed 	/*
388beec8214SDarren Reed 	 * Check if we want to allow this packet to be processed.
389beec8214SDarren Reed 	 * Consider it to be bad if not.
390beec8214SDarren Reed 	 */
3911ee25934SPeter Wemm 	if (fr_checkp) {
392beec8214SDarren Reed 		struct	mbuf	*m1 = m;
393df8bae1dSRodney W. Grimes 
394beec8214SDarren Reed 		if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m1) || !m1)
395beec8214SDarren Reed 			return;
396beec8214SDarren Reed 		ip = mtod(m = m1, struct ip *);
397beec8214SDarren Reed 	}
398beec8214SDarren Reed #endif
39958938916SGarrett Wollman #ifdef COMPAT_IPFW
40093e0e116SJulian Elischer 	if (ip_fw_chk_ptr) {
401f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
402f9e354dfSJulian Elischer 		/*
403f9e354dfSJulian Elischer 		 * If we've been forwarded from the output side, then
404f9e354dfSJulian Elischer 		 * skip the firewall a second time
405f9e354dfSJulian Elischer 		 */
406f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr)
407f9e354dfSJulian Elischer 			goto ours;
408f9e354dfSJulian Elischer #endif	/* IPFIREWALL_FORWARD */
409b715f178SLuigi Rizzo 		i = (*ip_fw_chk_ptr)(&ip, hlen, NULL, &ip_divert_cookie,
410b715f178SLuigi Rizzo 					&m, &rule, &ip_fw_fwd_addr);
411f9e354dfSJulian Elischer 		/*
412b715f178SLuigi Rizzo 		 * see the comment in ip_output for the return values
413b715f178SLuigi Rizzo 		 * produced by the firewall.
414f9e354dfSJulian Elischer 		 */
415b715f178SLuigi Rizzo 		if (!m) /* packet discarded by firewall */
416b715f178SLuigi Rizzo 			return ;
417b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr == NULL) /* common case */
418b715f178SLuigi Rizzo 			goto pass ;
419b715f178SLuigi Rizzo #ifdef DUMMYNET
420b715f178SLuigi Rizzo                 if (i & 0x10000) {
421b715f178SLuigi Rizzo                         /* send packet to the appropriate pipe */
422b715f178SLuigi Rizzo                         dummynet_io(i&0xffff,DN_TO_IP_IN,m,NULL,NULL,0, rule);
423e4676ba6SJulian Elischer 			return ;
42493e0e116SJulian Elischer 		}
425b715f178SLuigi Rizzo #endif
426b715f178SLuigi Rizzo #ifdef IPDIVERT
427b715f178SLuigi Rizzo 		if (i > 0 && i < 0x10000) {
428b715f178SLuigi Rizzo 			/* Divert packet */
429b715f178SLuigi Rizzo 			frag_divert_port = i & 0xffff ;
430b715f178SLuigi Rizzo 			goto ours;
431b715f178SLuigi Rizzo 		}
432b715f178SLuigi Rizzo #endif
433b715f178SLuigi Rizzo #ifdef IPFIREWALL_FORWARD
434b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr != NULL)
435b715f178SLuigi Rizzo 			goto pass ;
436b715f178SLuigi Rizzo #endif
437b715f178SLuigi Rizzo 		/*
438b715f178SLuigi Rizzo 		 * if we get here, the packet must be dropped
439b715f178SLuigi Rizzo 		 */
440b715f178SLuigi Rizzo 			m_freem(m);
441b715f178SLuigi Rizzo 			return;
442b715f178SLuigi Rizzo 	}
443b715f178SLuigi Rizzo pass:
444100ba1a6SJordan K. Hubbard 
4456713d4a7SSøren Schmidt         if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN))
446fed1c7e9SSøren Schmidt 		return;
447f9e354dfSJulian Elischer #endif	/* !COMPAT_IPFW */
448fed1c7e9SSøren Schmidt 
449df8bae1dSRodney W. Grimes 	/*
450df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
451df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
452df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
453df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
454df8bae1dSRodney W. Grimes 	 */
455df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
456df8bae1dSRodney W. Grimes 	if (hlen > sizeof (struct ip) && ip_dooptions(m))
457c67b1d17SGarrett Wollman 		return;
458df8bae1dSRodney W. Grimes 
459f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
460f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
461f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
462f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
463f0068c4aSGarrett Wollman 	 * grabbing the packet.
464f0068c4aSGarrett Wollman          */
4651c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
466f0068c4aSGarrett Wollman 		goto ours;
467f0068c4aSGarrett Wollman 
468df8bae1dSRodney W. Grimes 	/*
469df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
470df8bae1dSRodney W. Grimes 	 */
471d4295c32SJulian Elischer 	for (ia = TAILQ_FIRST(&in_ifaddrhead); ia;
472f9e354dfSJulian Elischer 					ia = TAILQ_NEXT(ia, ia_link)) {
473df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
474df8bae1dSRodney W. Grimes 
475df8bae1dSRodney W. Grimes 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
476df8bae1dSRodney W. Grimes 			goto ours;
477432aad0eSTor Egge #ifdef BOOTP_COMPAT
478432aad0eSTor Egge 		if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
479432aad0eSTor Egge 			goto ours;
480432aad0eSTor Egge #endif
481f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
482f9e354dfSJulian Elischer 		/*
483f9e354dfSJulian Elischer 		 * If the addr to forward to is one of ours, we pretend to
484f9e354dfSJulian Elischer 		 * be the destination for this packet.
485f9e354dfSJulian Elischer 		 */
486f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr != NULL &&
487f9e354dfSJulian Elischer 			IA_SIN(ia)->sin_addr.s_addr ==
488f9e354dfSJulian Elischer 					 ip_fw_fwd_addr->sin_addr.s_addr)
489f9e354dfSJulian Elischer 			goto ours;
490f9e354dfSJulian Elischer #endif
4916ed666afSPoul-Henning Kamp 		if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
492df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
493df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr)
494df8bae1dSRodney W. Grimes 				goto ours;
495df8bae1dSRodney W. Grimes 			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
496df8bae1dSRodney W. Grimes 				goto ours;
497df8bae1dSRodney W. Grimes 		}
498df8bae1dSRodney W. Grimes 	}
499df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
500df8bae1dSRodney W. Grimes 		struct in_multi *inm;
501df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
502df8bae1dSRodney W. Grimes 			/*
503df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
504df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
505df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
506df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
507df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
508df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
509df8bae1dSRodney W. Grimes 			 *
510df8bae1dSRodney W. Grimes 			 * (The IP ident field is put in the same byte order
511df8bae1dSRodney W. Grimes 			 * as expected when ip_mforward() is called from
512df8bae1dSRodney W. Grimes 			 * ip_output().)
513df8bae1dSRodney W. Grimes 			 */
514df8bae1dSRodney W. Grimes 			ip->ip_id = htons(ip->ip_id);
515f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
516df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
517df8bae1dSRodney W. Grimes 				m_freem(m);
518c67b1d17SGarrett Wollman 				return;
519df8bae1dSRodney W. Grimes 			}
520df8bae1dSRodney W. Grimes 			ip->ip_id = ntohs(ip->ip_id);
521df8bae1dSRodney W. Grimes 
522df8bae1dSRodney W. Grimes 			/*
523df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
524df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
525df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
526df8bae1dSRodney W. Grimes 			 */
527df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
528df8bae1dSRodney W. Grimes 				goto ours;
529df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
530df8bae1dSRodney W. Grimes 		}
531df8bae1dSRodney W. Grimes 		/*
532df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
533df8bae1dSRodney W. Grimes 		 * arrival interface.
534df8bae1dSRodney W. Grimes 		 */
535df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
536df8bae1dSRodney W. Grimes 		if (inm == NULL) {
53782c39223SGarrett Wollman 			ipstat.ips_notmember++;
538df8bae1dSRodney W. Grimes 			m_freem(m);
539c67b1d17SGarrett Wollman 			return;
540df8bae1dSRodney W. Grimes 		}
541df8bae1dSRodney W. Grimes 		goto ours;
542df8bae1dSRodney W. Grimes 	}
543df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
544df8bae1dSRodney W. Grimes 		goto ours;
545df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
546df8bae1dSRodney W. Grimes 		goto ours;
547df8bae1dSRodney W. Grimes 
548df8bae1dSRodney W. Grimes 	/*
549df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
550df8bae1dSRodney W. Grimes 	 */
551df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
552df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
553df8bae1dSRodney W. Grimes 		m_freem(m);
554df8bae1dSRodney W. Grimes 	} else
555df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
556c67b1d17SGarrett Wollman 	return;
557df8bae1dSRodney W. Grimes 
558df8bae1dSRodney W. Grimes ours:
559100ba1a6SJordan K. Hubbard 
56063f8d699SJordan K. Hubbard 	/*
561df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
562df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
563df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
564df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
565df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
566df8bae1dSRodney W. Grimes 	 */
567c383a33fSDima Ruban 	if (ip->ip_off & (IP_MF | IP_OFFMASK | IP_RF)) {
568df8bae1dSRodney W. Grimes 		if (m->m_flags & M_EXT) {		/* XXX */
569b715f178SLuigi Rizzo 			if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
570df8bae1dSRodney W. Grimes 				ipstat.ips_toosmall++;
57193e0e116SJulian Elischer #ifdef IPDIVERT
57293e0e116SJulian Elischer 				frag_divert_port = 0;
573c977d4c7SJulian Elischer 				ip_divert_cookie = 0;
57493e0e116SJulian Elischer #endif
575c67b1d17SGarrett Wollman 				return;
576df8bae1dSRodney W. Grimes 			}
577df8bae1dSRodney W. Grimes 			ip = mtod(m, struct ip *);
578df8bae1dSRodney W. Grimes 		}
579194a213eSAndrey A. Chernov 		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
580df8bae1dSRodney W. Grimes 		/*
581df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
582df8bae1dSRodney W. Grimes 		 * of this datagram.
583df8bae1dSRodney W. Grimes 		 */
584194a213eSAndrey A. Chernov 		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
585df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
586df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
587df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
588df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
589df8bae1dSRodney W. Grimes 				goto found;
590df8bae1dSRodney W. Grimes 
591194a213eSAndrey A. Chernov 		fp = 0;
592194a213eSAndrey A. Chernov 
593194a213eSAndrey A. Chernov 		/* check if there's a place for the new queue */
594194a213eSAndrey A. Chernov 		if (nipq > maxnipq) {
595194a213eSAndrey A. Chernov 		    /*
596194a213eSAndrey A. Chernov 		     * drop something from the tail of the current queue
597194a213eSAndrey A. Chernov 		     * before proceeding further
598194a213eSAndrey A. Chernov 		     */
599194a213eSAndrey A. Chernov 		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */
600194a213eSAndrey A. Chernov 			for (i = 0; i < IPREASS_NHASH; i++) {
601194a213eSAndrey A. Chernov 			    if (ipq[i].prev != &ipq[i]) {
602194a213eSAndrey A. Chernov 				ip_freef(ipq[i].prev);
603194a213eSAndrey A. Chernov 				break;
604194a213eSAndrey A. Chernov 			    }
605194a213eSAndrey A. Chernov 			}
606194a213eSAndrey A. Chernov 		    } else
607194a213eSAndrey A. Chernov 			ip_freef(ipq[sum].prev);
608194a213eSAndrey A. Chernov 		}
609194a213eSAndrey A. Chernov found:
610df8bae1dSRodney W. Grimes 		/*
611df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
612df8bae1dSRodney W. Grimes 		 * set ip_mff if more fragments are expected,
613df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
614df8bae1dSRodney W. Grimes 		 */
615df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
6166effc713SDoug Rabson 		mff = (ip->ip_off & IP_MF) != 0;
6176effc713SDoug Rabson 		if (mff) {
6186effc713SDoug Rabson 		        /*
6196effc713SDoug Rabson 		         * Make sure that fragments have a data length
6206effc713SDoug Rabson 			 * that's a non-zero multiple of 8 bytes.
6216effc713SDoug Rabson 		         */
6226effc713SDoug Rabson 			if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
6236effc713SDoug Rabson 				ipstat.ips_toosmall++; /* XXX */
6246effc713SDoug Rabson 				goto bad;
6256effc713SDoug Rabson 			}
6266effc713SDoug Rabson 			m->m_flags |= M_FRAG;
6276effc713SDoug Rabson 		}
628df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
629df8bae1dSRodney W. Grimes 
630df8bae1dSRodney W. Grimes 		/*
631df8bae1dSRodney W. Grimes 		 * If datagram marked as having more fragments
632df8bae1dSRodney W. Grimes 		 * or if this is not the first fragment,
633df8bae1dSRodney W. Grimes 		 * attempt reassembly; if it succeeds, proceed.
634df8bae1dSRodney W. Grimes 		 */
6356effc713SDoug Rabson 		if (mff || ip->ip_off) {
636df8bae1dSRodney W. Grimes 			ipstat.ips_fragments++;
6376effc713SDoug Rabson 			m->m_pkthdr.header = ip;
6386effc713SDoug Rabson 			ip = ip_reass(m, fp, &ipq[sum]);
639f9e354dfSJulian Elischer 			if (ip == 0) {
640f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
641f9e354dfSJulian Elischer 				ip_fw_fwd_addr = NULL;
642f9e354dfSJulian Elischer #endif
643c67b1d17SGarrett Wollman 				return;
644f9e354dfSJulian Elischer 			}
64581aee63dSPoul-Henning Kamp 			/* Get the length of the reassembled packets header */
64681aee63dSPoul-Henning Kamp 			hlen = IP_VHL_HL(ip->ip_vhl) << 2;
647df8bae1dSRodney W. Grimes 			ipstat.ips_reassembled++;
648df8bae1dSRodney W. Grimes 			m = dtom(ip);
649af782f1cSBrian Somers #ifdef IPDIVERT
650af782f1cSBrian Somers 			if (frag_divert_port) {
651af782f1cSBrian Somers 				ip->ip_len += hlen;
652af782f1cSBrian Somers 				HTONS(ip->ip_len);
653af782f1cSBrian Somers 				HTONS(ip->ip_off);
654af782f1cSBrian Somers 				HTONS(ip->ip_id);
655af782f1cSBrian Somers 				ip->ip_sum = 0;
656af782f1cSBrian Somers 				ip->ip_sum = in_cksum_hdr(ip);
657af782f1cSBrian Somers 				NTOHS(ip->ip_id);
658af782f1cSBrian Somers 				NTOHS(ip->ip_off);
659af782f1cSBrian Somers 				NTOHS(ip->ip_len);
660af782f1cSBrian Somers 				ip->ip_len -= hlen;
661af782f1cSBrian Somers 			}
662af782f1cSBrian Somers #endif
663df8bae1dSRodney W. Grimes 		} else
664df8bae1dSRodney W. Grimes 			if (fp)
665df8bae1dSRodney W. Grimes 				ip_freef(fp);
666df8bae1dSRodney W. Grimes 	} else
667df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
668df8bae1dSRodney W. Grimes 
66993e0e116SJulian Elischer #ifdef IPDIVERT
67093e0e116SJulian Elischer 	/*
671e4676ba6SJulian Elischer 	 * Divert reassembled packets to the divert protocol if required
672efe39c6aSJulian Elischer 	 *  If divert port is null then cookie should be too,
673efe39c6aSJulian Elischer 	 * so we shouldn't need to clear them here. Assume ip_divert does so.
67493e0e116SJulian Elischer 	 */
67593e0e116SJulian Elischer 	if (frag_divert_port) {
676e4676ba6SJulian Elischer 		ipstat.ips_delivered++;
67793e0e116SJulian Elischer 		ip_divert_port = frag_divert_port;
67893e0e116SJulian Elischer 		frag_divert_port = 0;
67993e0e116SJulian Elischer 		(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);
68093e0e116SJulian Elischer 		return;
68193e0e116SJulian Elischer 	}
68279755dc5SJulian Elischer 
68379755dc5SJulian Elischer 	/* Don't let packets divert themselves */
68479755dc5SJulian Elischer 	if (ip->ip_p == IPPROTO_DIVERT) {
68579755dc5SJulian Elischer 		ipstat.ips_noproto++;
68679755dc5SJulian Elischer 		goto bad;
68779755dc5SJulian Elischer 	}
688bb60f459SJulian Elischer 
68993e0e116SJulian Elischer #endif
69093e0e116SJulian Elischer 
691df8bae1dSRodney W. Grimes 	/*
692df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
693df8bae1dSRodney W. Grimes 	 */
694df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
695df8bae1dSRodney W. Grimes 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
696f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
697f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;	/* tcp needed it */
698f9e354dfSJulian Elischer #endif
699c67b1d17SGarrett Wollman 	return;
700df8bae1dSRodney W. Grimes bad:
701f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
702f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;
703f9e354dfSJulian Elischer #endif
704df8bae1dSRodney W. Grimes 	m_freem(m);
705c67b1d17SGarrett Wollman }
706c67b1d17SGarrett Wollman 
707c67b1d17SGarrett Wollman /*
708c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
709c67b1d17SGarrett Wollman  */
710c67b1d17SGarrett Wollman static void
711c67b1d17SGarrett Wollman ipintr(void)
712c67b1d17SGarrett Wollman {
713c67b1d17SGarrett Wollman 	int s;
714c67b1d17SGarrett Wollman 	struct mbuf *m;
715c67b1d17SGarrett Wollman 
716c67b1d17SGarrett Wollman 	while(1) {
717c67b1d17SGarrett Wollman 		s = splimp();
718c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
719c67b1d17SGarrett Wollman 		splx(s);
720c67b1d17SGarrett Wollman 		if (m == 0)
721c67b1d17SGarrett Wollman 			return;
722c67b1d17SGarrett Wollman 		ip_input(m);
723c67b1d17SGarrett Wollman 	}
724df8bae1dSRodney W. Grimes }
725df8bae1dSRodney W. Grimes 
726748e0b0aSGarrett Wollman NETISR_SET(NETISR_IP, ipintr);
727748e0b0aSGarrett Wollman 
728df8bae1dSRodney W. Grimes /*
729df8bae1dSRodney W. Grimes  * Take incoming datagram fragment and try to
730df8bae1dSRodney W. Grimes  * reassemble it into whole datagram.  If a chain for
731df8bae1dSRodney W. Grimes  * reassembly of this datagram already exists, then it
732df8bae1dSRodney W. Grimes  * is given as fp; otherwise have to make a chain.
733df8bae1dSRodney W. Grimes  */
7340312fbe9SPoul-Henning Kamp static struct ip *
7356effc713SDoug Rabson ip_reass(m, fp, where)
7366effc713SDoug Rabson 	register struct mbuf *m;
737df8bae1dSRodney W. Grimes 	register struct ipq *fp;
738194a213eSAndrey A. Chernov 	struct   ipq    *where;
739df8bae1dSRodney W. Grimes {
7406effc713SDoug Rabson 	struct ip *ip = mtod(m, struct ip *);
7416effc713SDoug Rabson 	register struct mbuf *p = 0, *q, *nq;
742df8bae1dSRodney W. Grimes 	struct mbuf *t;
7436effc713SDoug Rabson 	int hlen = IP_VHL_HL(ip->ip_vhl) << 2;
744df8bae1dSRodney W. Grimes 	int i, next;
745df8bae1dSRodney W. Grimes 
746df8bae1dSRodney W. Grimes 	/*
747df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
748df8bae1dSRodney W. Grimes 	 * would confuse code below.
749df8bae1dSRodney W. Grimes 	 */
750df8bae1dSRodney W. Grimes 	m->m_data += hlen;
751df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
752df8bae1dSRodney W. Grimes 
753df8bae1dSRodney W. Grimes 	/*
754df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
755df8bae1dSRodney W. Grimes 	 */
756df8bae1dSRodney W. Grimes 	if (fp == 0) {
757df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
758df8bae1dSRodney W. Grimes 			goto dropfrag;
759df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
760194a213eSAndrey A. Chernov 		insque(fp, where);
761194a213eSAndrey A. Chernov 		nipq++;
762df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
763df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
764df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
7656effc713SDoug Rabson 		fp->ipq_src = ip->ip_src;
7666effc713SDoug Rabson 		fp->ipq_dst = ip->ip_dst;
767b715f178SLuigi Rizzo 		fp->ipq_frags = 0;
76893e0e116SJulian Elischer #ifdef IPDIVERT
76993e0e116SJulian Elischer 		fp->ipq_divert = 0;
770bb60f459SJulian Elischer 		fp->ipq_div_cookie = 0;
77193e0e116SJulian Elischer #endif
772b715f178SLuigi Rizzo 		q = 0;
773b715f178SLuigi Rizzo 		goto insert;
774df8bae1dSRodney W. Grimes 	}
775df8bae1dSRodney W. Grimes 
7766effc713SDoug Rabson #define GETIP(m)	((struct ip*)((m)->m_pkthdr.header))
7776effc713SDoug Rabson 
778df8bae1dSRodney W. Grimes 	/*
779df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
780df8bae1dSRodney W. Grimes 	 */
7816effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
7826effc713SDoug Rabson 		if (GETIP(q)->ip_off > ip->ip_off)
783df8bae1dSRodney W. Grimes 			break;
784df8bae1dSRodney W. Grimes 
785df8bae1dSRodney W. Grimes 	/*
786df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
787df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
788b715f178SLuigi Rizzo 	 * segment.  If it provides all of our data, drop us.
789df8bae1dSRodney W. Grimes 	 */
7906effc713SDoug Rabson 	if (p) {
7916effc713SDoug Rabson 		i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;
792df8bae1dSRodney W. Grimes 		if (i > 0) {
793df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
794df8bae1dSRodney W. Grimes 				goto dropfrag;
795df8bae1dSRodney W. Grimes 			m_adj(dtom(ip), i);
796df8bae1dSRodney W. Grimes 			ip->ip_off += i;
797df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
798df8bae1dSRodney W. Grimes 		}
799df8bae1dSRodney W. Grimes 	}
800df8bae1dSRodney W. Grimes 
801df8bae1dSRodney W. Grimes 	/*
802df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
803df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
804df8bae1dSRodney W. Grimes 	 */
8056effc713SDoug Rabson 	for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;
806b715f178SLuigi Rizzo 	     p = q, q = nq) {
8076effc713SDoug Rabson 		i = (ip->ip_off + ip->ip_len) -
8086effc713SDoug Rabson 		    GETIP(q)->ip_off;
8096effc713SDoug Rabson 		if (i < GETIP(q)->ip_len) {
8106effc713SDoug Rabson 			GETIP(q)->ip_len -= i;
8116effc713SDoug Rabson 			GETIP(q)->ip_off += i;
8126effc713SDoug Rabson 			m_adj(q, i);
813df8bae1dSRodney W. Grimes 			break;
814df8bae1dSRodney W. Grimes 		}
8156effc713SDoug Rabson 		nq = q->m_nextpkt;
816b715f178SLuigi Rizzo 		if (p)
817b715f178SLuigi Rizzo 			p->m_nextpkt = nq;
818b715f178SLuigi Rizzo 		else
819b715f178SLuigi Rizzo 			fp->ipq_frags = nq;
8206effc713SDoug Rabson 		m_freem(q);
821df8bae1dSRodney W. Grimes 	}
822df8bae1dSRodney W. Grimes 
823b715f178SLuigi Rizzo insert:
82493e0e116SJulian Elischer 
82593e0e116SJulian Elischer #ifdef IPDIVERT
82693e0e116SJulian Elischer 	/*
82793e0e116SJulian Elischer 	 * Any fragment diverting causes the whole packet to divert
82893e0e116SJulian Elischer 	 */
829efe39c6aSJulian Elischer 	if (frag_divert_port) {
83093e0e116SJulian Elischer 		fp->ipq_divert = frag_divert_port;
831c977d4c7SJulian Elischer 		fp->ipq_div_cookie = ip_divert_cookie;
832bb60f459SJulian Elischer 	}
83393e0e116SJulian Elischer 	frag_divert_port = 0;
834c977d4c7SJulian Elischer 	ip_divert_cookie = 0;
83593e0e116SJulian Elischer #endif
83693e0e116SJulian Elischer 
837df8bae1dSRodney W. Grimes 	/*
838b715f178SLuigi Rizzo 	 * Stick new segment in its place;
839b715f178SLuigi Rizzo 	 * check for complete reassembly.
840df8bae1dSRodney W. Grimes 	 */
841b715f178SLuigi Rizzo 	if (p == NULL) {
842b715f178SLuigi Rizzo 		m->m_nextpkt = fp->ipq_frags;
843b715f178SLuigi Rizzo 		fp->ipq_frags = m;
844b715f178SLuigi Rizzo 	} else {
845b715f178SLuigi Rizzo 		m->m_nextpkt = p->m_nextpkt;
846b715f178SLuigi Rizzo 		p->m_nextpkt = m;
847b715f178SLuigi Rizzo 	}
8486effc713SDoug Rabson 	next = 0;
8496effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
8506effc713SDoug Rabson 		if (GETIP(q)->ip_off != next)
8516effc713SDoug Rabson 			return (0);
8526effc713SDoug Rabson 		next += GETIP(q)->ip_len;
8536effc713SDoug Rabson 	}
8546effc713SDoug Rabson 	/* Make sure the last packet didn't have the IP_MF flag */
8556effc713SDoug Rabson 	if (p->m_flags & M_FRAG)
856df8bae1dSRodney W. Grimes 		return (0);
857df8bae1dSRodney W. Grimes 
858df8bae1dSRodney W. Grimes 	/*
859430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
860430d30d8SBill Fenner 	 */
8616effc713SDoug Rabson 	q = fp->ipq_frags;
8626effc713SDoug Rabson 	ip = GETIP(q);
8636effc713SDoug Rabson 	if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) {
864430d30d8SBill Fenner 		ipstat.ips_toolong++;
865430d30d8SBill Fenner 		ip_freef(fp);
866430d30d8SBill Fenner 		return (0);
867430d30d8SBill Fenner 	}
868430d30d8SBill Fenner 
869430d30d8SBill Fenner 	/*
870430d30d8SBill Fenner 	 * Concatenate fragments.
871df8bae1dSRodney W. Grimes 	 */
8726effc713SDoug Rabson 	m = q;
873df8bae1dSRodney W. Grimes 	t = m->m_next;
874df8bae1dSRodney W. Grimes 	m->m_next = 0;
875df8bae1dSRodney W. Grimes 	m_cat(m, t);
8766effc713SDoug Rabson 	nq = q->m_nextpkt;
877945aa40dSDoug Rabson 	q->m_nextpkt = 0;
8786effc713SDoug Rabson 	for (q = nq; q != NULL; q = nq) {
8796effc713SDoug Rabson 		nq = q->m_nextpkt;
880945aa40dSDoug Rabson 		q->m_nextpkt = NULL;
8816effc713SDoug Rabson 		m_cat(m, q);
882df8bae1dSRodney W. Grimes 	}
883df8bae1dSRodney W. Grimes 
88493e0e116SJulian Elischer #ifdef IPDIVERT
88593e0e116SJulian Elischer 	/*
886c977d4c7SJulian Elischer 	 * extract divert port for packet, if any
88793e0e116SJulian Elischer 	 */
88893e0e116SJulian Elischer 	frag_divert_port = fp->ipq_divert;
889c977d4c7SJulian Elischer 	ip_divert_cookie = fp->ipq_div_cookie;
89093e0e116SJulian Elischer #endif
89193e0e116SJulian Elischer 
892df8bae1dSRodney W. Grimes 	/*
893df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
894df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
895df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
896df8bae1dSRodney W. Grimes 	 * Make header visible.
897df8bae1dSRodney W. Grimes 	 */
898df8bae1dSRodney W. Grimes 	ip->ip_len = next;
8996effc713SDoug Rabson 	ip->ip_src = fp->ipq_src;
9006effc713SDoug Rabson 	ip->ip_dst = fp->ipq_dst;
901df8bae1dSRodney W. Grimes 	remque(fp);
902194a213eSAndrey A. Chernov 	nipq--;
903df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
9046effc713SDoug Rabson 	m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
9056effc713SDoug Rabson 	m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);
906df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
907df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
908df8bae1dSRodney W. Grimes 		register int plen = 0;
909df8bae1dSRodney W. Grimes 		for (t = m; m; m = m->m_next)
910df8bae1dSRodney W. Grimes 			plen += m->m_len;
911df8bae1dSRodney W. Grimes 		t->m_pkthdr.len = plen;
912df8bae1dSRodney W. Grimes 	}
9136effc713SDoug Rabson 	return (ip);
914df8bae1dSRodney W. Grimes 
915df8bae1dSRodney W. Grimes dropfrag:
916efe39c6aSJulian Elischer #ifdef IPDIVERT
917efe39c6aSJulian Elischer 	frag_divert_port = 0;
918efe39c6aSJulian Elischer 	ip_divert_cookie = 0;
919efe39c6aSJulian Elischer #endif
920df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
921df8bae1dSRodney W. Grimes 	m_freem(m);
922df8bae1dSRodney W. Grimes 	return (0);
9236effc713SDoug Rabson 
9246effc713SDoug Rabson #undef GETIP
925df8bae1dSRodney W. Grimes }
926df8bae1dSRodney W. Grimes 
927df8bae1dSRodney W. Grimes /*
928df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
929df8bae1dSRodney W. Grimes  * associated datagrams.
930df8bae1dSRodney W. Grimes  */
9310312fbe9SPoul-Henning Kamp static void
932df8bae1dSRodney W. Grimes ip_freef(fp)
933df8bae1dSRodney W. Grimes 	struct ipq *fp;
934df8bae1dSRodney W. Grimes {
9356effc713SDoug Rabson 	register struct mbuf *q;
936df8bae1dSRodney W. Grimes 
9376effc713SDoug Rabson 	while (fp->ipq_frags) {
9386effc713SDoug Rabson 		q = fp->ipq_frags;
9396effc713SDoug Rabson 		fp->ipq_frags = q->m_nextpkt;
9406effc713SDoug Rabson 		m_freem(q);
941df8bae1dSRodney W. Grimes 	}
942df8bae1dSRodney W. Grimes 	remque(fp);
943df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
944194a213eSAndrey A. Chernov 	nipq--;
945df8bae1dSRodney W. Grimes }
946df8bae1dSRodney W. Grimes 
947df8bae1dSRodney W. Grimes /*
948df8bae1dSRodney W. Grimes  * IP timer processing;
949df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
950df8bae1dSRodney W. Grimes  * queue, discard it.
951df8bae1dSRodney W. Grimes  */
952df8bae1dSRodney W. Grimes void
953df8bae1dSRodney W. Grimes ip_slowtimo()
954df8bae1dSRodney W. Grimes {
955df8bae1dSRodney W. Grimes 	register struct ipq *fp;
956df8bae1dSRodney W. Grimes 	int s = splnet();
957194a213eSAndrey A. Chernov 	int i;
958df8bae1dSRodney W. Grimes 
959194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
960194a213eSAndrey A. Chernov 		fp = ipq[i].next;
961194a213eSAndrey A. Chernov 		if (fp == 0)
962194a213eSAndrey A. Chernov 			continue;
963194a213eSAndrey A. Chernov 		while (fp != &ipq[i]) {
964df8bae1dSRodney W. Grimes 			--fp->ipq_ttl;
965df8bae1dSRodney W. Grimes 			fp = fp->next;
966df8bae1dSRodney W. Grimes 			if (fp->prev->ipq_ttl == 0) {
967df8bae1dSRodney W. Grimes 				ipstat.ips_fragtimeout++;
968df8bae1dSRodney W. Grimes 				ip_freef(fp->prev);
969df8bae1dSRodney W. Grimes 			}
970df8bae1dSRodney W. Grimes 		}
971194a213eSAndrey A. Chernov 	}
9721f91d8c5SDavid Greenman 	ipflow_slowtimo();
973df8bae1dSRodney W. Grimes 	splx(s);
974df8bae1dSRodney W. Grimes }
975df8bae1dSRodney W. Grimes 
976df8bae1dSRodney W. Grimes /*
977df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
978df8bae1dSRodney W. Grimes  */
979df8bae1dSRodney W. Grimes void
980df8bae1dSRodney W. Grimes ip_drain()
981df8bae1dSRodney W. Grimes {
982194a213eSAndrey A. Chernov 	int     i;
983ce29ab3aSGarrett Wollman 
984194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
985194a213eSAndrey A. Chernov 		while (ipq[i].next != &ipq[i]) {
986194a213eSAndrey A. Chernov 			ipstat.ips_fragdropped++;
987194a213eSAndrey A. Chernov 			ip_freef(ipq[i].next);
988194a213eSAndrey A. Chernov 		}
989194a213eSAndrey A. Chernov 	}
990ce29ab3aSGarrett Wollman 	in_rtqdrain();
991df8bae1dSRodney W. Grimes }
992df8bae1dSRodney W. Grimes 
993df8bae1dSRodney W. Grimes /*
994df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
995df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
996df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
997df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
998df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
999df8bae1dSRodney W. Grimes  */
10000312fbe9SPoul-Henning Kamp static int
1001df8bae1dSRodney W. Grimes ip_dooptions(m)
1002df8bae1dSRodney W. Grimes 	struct mbuf *m;
1003df8bae1dSRodney W. Grimes {
1004df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1005df8bae1dSRodney W. Grimes 	register u_char *cp;
1006df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
1007df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
1008df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
1009df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
1010df8bae1dSRodney W. Grimes 	n_time ntime;
1011df8bae1dSRodney W. Grimes 
1012df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
1013df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
101458938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1015df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
1016df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
1017df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
1018df8bae1dSRodney W. Grimes 			break;
1019df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
1020df8bae1dSRodney W. Grimes 			optlen = 1;
1021df8bae1dSRodney W. Grimes 		else {
1022df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
1023df8bae1dSRodney W. Grimes 			if (optlen <= 0 || optlen > cnt) {
1024df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1025df8bae1dSRodney W. Grimes 				goto bad;
1026df8bae1dSRodney W. Grimes 			}
1027df8bae1dSRodney W. Grimes 		}
1028df8bae1dSRodney W. Grimes 		switch (opt) {
1029df8bae1dSRodney W. Grimes 
1030df8bae1dSRodney W. Grimes 		default:
1031df8bae1dSRodney W. Grimes 			break;
1032df8bae1dSRodney W. Grimes 
1033df8bae1dSRodney W. Grimes 		/*
1034df8bae1dSRodney W. Grimes 		 * Source routing with record.
1035df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
1036df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
1037df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
1038df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
1039df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
1040df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
1041df8bae1dSRodney W. Grimes 		 */
1042df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
1043df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
1044df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1045df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1046df8bae1dSRodney W. Grimes 				goto bad;
1047df8bae1dSRodney W. Grimes 			}
1048df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
1049df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
1050df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
1051df8bae1dSRodney W. Grimes 			if (ia == 0) {
1052df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
1053df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
1054df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
1055df8bae1dSRodney W. Grimes 					goto bad;
1056df8bae1dSRodney W. Grimes 				}
1057bc189bf8SGuido van Rooij 				if (!ip_dosourceroute)
1058bc189bf8SGuido van Rooij 					goto nosourcerouting;
1059df8bae1dSRodney W. Grimes 				/*
1060df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
1061df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
1062df8bae1dSRodney W. Grimes 				 */
1063df8bae1dSRodney W. Grimes 				break;
1064df8bae1dSRodney W. Grimes 			}
1065df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1066df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr)) {
1067df8bae1dSRodney W. Grimes 				/*
1068df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
1069df8bae1dSRodney W. Grimes 				 */
10704fce5804SGuido van Rooij 				if (!ip_acceptsourceroute)
10714fce5804SGuido van Rooij 					goto nosourcerouting;
1072df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
1073df8bae1dSRodney W. Grimes 				break;
1074df8bae1dSRodney W. Grimes 			}
10751025071fSGarrett Wollman 
10761025071fSGarrett Wollman 			if (!ip_dosourceroute) {
10770af8d3ecSDavid Greenman 				if (ipforwarding) {
10780af8d3ecSDavid Greenman 					char buf[16]; /* aaa.bbb.ccc.ddd\0 */
10790af8d3ecSDavid Greenman 					/*
10800af8d3ecSDavid Greenman 					 * Acting as a router, so generate ICMP
10810af8d3ecSDavid Greenman 					 */
1082efa48587SGuido van Rooij nosourcerouting:
1083bc189bf8SGuido van Rooij 					strcpy(buf, inet_ntoa(ip->ip_dst));
10841025071fSGarrett Wollman 					log(LOG_WARNING,
10851025071fSGarrett Wollman 					    "attempted source route from %s to %s\n",
10861025071fSGarrett Wollman 					    inet_ntoa(ip->ip_src), buf);
10871025071fSGarrett Wollman 					type = ICMP_UNREACH;
10881025071fSGarrett Wollman 					code = ICMP_UNREACH_SRCFAIL;
10891025071fSGarrett Wollman 					goto bad;
10900af8d3ecSDavid Greenman 				} else {
10910af8d3ecSDavid Greenman 					/*
10920af8d3ecSDavid Greenman 					 * Not acting as a router, so silently drop.
10930af8d3ecSDavid Greenman 					 */
10940af8d3ecSDavid Greenman 					ipstat.ips_cantforward++;
10950af8d3ecSDavid Greenman 					m_freem(m);
10960af8d3ecSDavid Greenman 					return (1);
10970af8d3ecSDavid Greenman 				}
10981025071fSGarrett Wollman 			}
10991025071fSGarrett Wollman 
1100df8bae1dSRodney W. Grimes 			/*
1101df8bae1dSRodney W. Grimes 			 * locate outgoing interface
1102df8bae1dSRodney W. Grimes 			 */
110394a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
1104df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
11051025071fSGarrett Wollman 
1106df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
1107df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
1108df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
1109df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
1110df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
1111df8bae1dSRodney W. Grimes 			} else
1112df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
1113df8bae1dSRodney W. Grimes 			if (ia == 0) {
1114df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1115df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
1116df8bae1dSRodney W. Grimes 				goto bad;
1117df8bae1dSRodney W. Grimes 			}
1118df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
111994a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
112094a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1121df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1122df8bae1dSRodney W. Grimes 			/*
1123df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
1124df8bae1dSRodney W. Grimes 			 */
1125df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1126df8bae1dSRodney W. Grimes 			break;
1127df8bae1dSRodney W. Grimes 
1128df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1129df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1130df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1131df8bae1dSRodney W. Grimes 				goto bad;
1132df8bae1dSRodney W. Grimes 			}
1133df8bae1dSRodney W. Grimes 			/*
1134df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1135df8bae1dSRodney W. Grimes 			 */
1136df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1137df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr))
1138df8bae1dSRodney W. Grimes 				break;
113994a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1140df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1141df8bae1dSRodney W. Grimes 			/*
1142df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1143df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1144df8bae1dSRodney W. Grimes 			 */
1145df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
1146df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
1147df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1148df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1149df8bae1dSRodney W. Grimes 				goto bad;
1150df8bae1dSRodney W. Grimes 			}
115194a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
115294a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1153df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1154df8bae1dSRodney W. Grimes 			break;
1155df8bae1dSRodney W. Grimes 
1156df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1157df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
1158df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
1159df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
1160df8bae1dSRodney W. Grimes 				goto bad;
11610c8d2590SBruce Evans 			if (ipt->ipt_ptr > ipt->ipt_len - sizeof(int32_t)) {
1162df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
1163df8bae1dSRodney W. Grimes 					goto bad;
1164df8bae1dSRodney W. Grimes 				break;
1165df8bae1dSRodney W. Grimes 			}
1166df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
1167df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
1168df8bae1dSRodney W. Grimes 
1169df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1170df8bae1dSRodney W. Grimes 				break;
1171df8bae1dSRodney W. Grimes 
1172df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
1173b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1174df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1175df8bae1dSRodney W. Grimes 					goto bad;
1176df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1177df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1178df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
1179df8bae1dSRodney W. Grimes 				if (ia == 0)
1180df8bae1dSRodney W. Grimes 					continue;
118194a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
118294a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
1183df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1184df8bae1dSRodney W. Grimes 				break;
1185df8bae1dSRodney W. Grimes 
1186df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1187b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1188df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1189df8bae1dSRodney W. Grimes 					goto bad;
119094a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1191df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1192df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1193df8bae1dSRodney W. Grimes 					continue;
1194df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1195df8bae1dSRodney W. Grimes 				break;
1196df8bae1dSRodney W. Grimes 
1197df8bae1dSRodney W. Grimes 			default:
1198df8bae1dSRodney W. Grimes 				goto bad;
1199df8bae1dSRodney W. Grimes 			}
1200df8bae1dSRodney W. Grimes 			ntime = iptime();
120194a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1202df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1203df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1204df8bae1dSRodney W. Grimes 		}
1205df8bae1dSRodney W. Grimes 	}
120647174b49SAndrey A. Chernov 	if (forward && ipforwarding) {
1207df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1208df8bae1dSRodney W. Grimes 		return (1);
1209df8bae1dSRodney W. Grimes 	}
1210df8bae1dSRodney W. Grimes 	return (0);
1211df8bae1dSRodney W. Grimes bad:
121258938916SGarrett Wollman 	ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2;   /* XXX icmp_error adds in hdr length */
1213df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1214df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1215df8bae1dSRodney W. Grimes 	return (1);
1216df8bae1dSRodney W. Grimes }
1217df8bae1dSRodney W. Grimes 
1218df8bae1dSRodney W. Grimes /*
1219df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1220df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1221df8bae1dSRodney W. Grimes  */
12220312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1223df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1224df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1225df8bae1dSRodney W. Grimes {
1226df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1227df8bae1dSRodney W. Grimes 
1228df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1229df8bae1dSRodney W. Grimes 
1230df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1231df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1232df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1233df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1234df8bae1dSRodney W. Grimes 		}
1235df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1236df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1237df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1238df8bae1dSRodney W. Grimes 
12392c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1240df8bae1dSRodney W. Grimes 	}
1241df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1242df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1243df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1244df8bae1dSRodney W. Grimes }
1245df8bae1dSRodney W. Grimes 
1246df8bae1dSRodney W. Grimes /*
1247df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1248df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1249df8bae1dSRodney W. Grimes  */
1250df8bae1dSRodney W. Grimes void
1251df8bae1dSRodney W. Grimes save_rte(option, dst)
1252df8bae1dSRodney W. Grimes 	u_char *option;
1253df8bae1dSRodney W. Grimes 	struct in_addr dst;
1254df8bae1dSRodney W. Grimes {
1255df8bae1dSRodney W. Grimes 	unsigned olen;
1256df8bae1dSRodney W. Grimes 
1257df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1258df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1259df8bae1dSRodney W. Grimes 	if (ipprintfs)
1260df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1261df8bae1dSRodney W. Grimes #endif
1262df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1263df8bae1dSRodney W. Grimes 		return;
12640453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1265df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1266df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1267df8bae1dSRodney W. Grimes }
1268df8bae1dSRodney W. Grimes 
1269df8bae1dSRodney W. Grimes /*
1270df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1271df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1272df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1273df8bae1dSRodney W. Grimes  */
1274df8bae1dSRodney W. Grimes struct mbuf *
1275df8bae1dSRodney W. Grimes ip_srcroute()
1276df8bae1dSRodney W. Grimes {
1277df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1278df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1279df8bae1dSRodney W. Grimes 
1280df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1281df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1282cfe8b629SGarrett Wollman 	m = m_get(M_DONTWAIT, MT_HEADER);
1283df8bae1dSRodney W. Grimes 	if (m == 0)
1284df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1285df8bae1dSRodney W. Grimes 
1286df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1287df8bae1dSRodney W. Grimes 
1288df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1289df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1290df8bae1dSRodney W. Grimes 	    OPTSIZ;
1291df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1292df8bae1dSRodney W. Grimes 	if (ipprintfs)
1293df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1294df8bae1dSRodney W. Grimes #endif
1295df8bae1dSRodney W. Grimes 
1296df8bae1dSRodney W. Grimes 	/*
1297df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1298df8bae1dSRodney W. Grimes 	 */
1299df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1300df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1301df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1302df8bae1dSRodney W. Grimes 	if (ipprintfs)
1303b715f178SLuigi Rizzo 		printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
1304df8bae1dSRodney W. Grimes #endif
1305df8bae1dSRodney W. Grimes 
1306df8bae1dSRodney W. Grimes 	/*
1307df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1308df8bae1dSRodney W. Grimes 	 */
1309df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1310df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
131194a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
131294a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1313df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1314df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1315df8bae1dSRodney W. Grimes #undef OPTSIZ
1316df8bae1dSRodney W. Grimes 	/*
1317df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1318df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1319df8bae1dSRodney W. Grimes 	 */
1320df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1321df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1322df8bae1dSRodney W. Grimes 		if (ipprintfs)
1323b715f178SLuigi Rizzo 			printf(" %lx", ntohl(q->s_addr));
1324df8bae1dSRodney W. Grimes #endif
1325df8bae1dSRodney W. Grimes 		*q++ = *p--;
1326df8bae1dSRodney W. Grimes 	}
1327df8bae1dSRodney W. Grimes 	/*
1328df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1329df8bae1dSRodney W. Grimes 	 */
1330df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1331df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1332df8bae1dSRodney W. Grimes 	if (ipprintfs)
1333b715f178SLuigi Rizzo 		printf(" %lx\n", ntohl(q->s_addr));
1334df8bae1dSRodney W. Grimes #endif
1335df8bae1dSRodney W. Grimes 	return (m);
1336df8bae1dSRodney W. Grimes }
1337df8bae1dSRodney W. Grimes 
1338df8bae1dSRodney W. Grimes /*
1339df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1340df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1341df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1342df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1343df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1344df8bae1dSRodney W. Grimes  */
1345df8bae1dSRodney W. Grimes void
1346df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1347df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1348df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1349df8bae1dSRodney W. Grimes {
1350df8bae1dSRodney W. Grimes 	register int i;
1351df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1352df8bae1dSRodney W. Grimes 	register caddr_t opts;
1353df8bae1dSRodney W. Grimes 	int olen;
1354df8bae1dSRodney W. Grimes 
135558938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1356df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1357df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1358df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1359df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1360df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1361df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
136258938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1363df8bae1dSRodney W. Grimes }
1364df8bae1dSRodney W. Grimes 
1365df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1366df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1367df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1368df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1369df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1370df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1371df8bae1dSRodney W. Grimes 	ENOPROTOOPT
1372df8bae1dSRodney W. Grimes };
1373df8bae1dSRodney W. Grimes 
1374df8bae1dSRodney W. Grimes /*
1375df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1376df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1377df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1378df8bae1dSRodney W. Grimes  * of codes and types.
1379df8bae1dSRodney W. Grimes  *
1380df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1381df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1382df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1383df8bae1dSRodney W. Grimes  * protocol deal with that.
1384df8bae1dSRodney W. Grimes  *
1385df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1386df8bae1dSRodney W. Grimes  * via a source route.
1387df8bae1dSRodney W. Grimes  */
13880312fbe9SPoul-Henning Kamp static void
1389df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1390df8bae1dSRodney W. Grimes 	struct mbuf *m;
1391df8bae1dSRodney W. Grimes 	int srcrt;
1392df8bae1dSRodney W. Grimes {
1393df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1394df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1395df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
139626f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1397df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1398df8bae1dSRodney W. Grimes 	n_long dest;
1399df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
1400df8bae1dSRodney W. Grimes 
1401df8bae1dSRodney W. Grimes 	dest = 0;
1402df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1403df8bae1dSRodney W. Grimes 	if (ipprintfs)
140461ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1405162886e2SBruce Evans 		    (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr,
1406162886e2SBruce Evans 		    ip->ip_ttl);
1407df8bae1dSRodney W. Grimes #endif
1408100ba1a6SJordan K. Hubbard 
1409100ba1a6SJordan K. Hubbard 
1410df8bae1dSRodney W. Grimes 	if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
1411df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1412df8bae1dSRodney W. Grimes 		m_freem(m);
1413df8bae1dSRodney W. Grimes 		return;
1414df8bae1dSRodney W. Grimes 	}
1415df8bae1dSRodney W. Grimes 	HTONS(ip->ip_id);
1416df8bae1dSRodney W. Grimes 	if (ip->ip_ttl <= IPTTLDEC) {
1417df8bae1dSRodney W. Grimes 		icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
1418df8bae1dSRodney W. Grimes 		return;
1419df8bae1dSRodney W. Grimes 	}
1420df8bae1dSRodney W. Grimes 	ip->ip_ttl -= IPTTLDEC;
1421df8bae1dSRodney W. Grimes 
1422df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1423df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1424df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1425df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1426df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1427df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1428df8bae1dSRodney W. Grimes 		}
1429df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1430df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1431df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1432df8bae1dSRodney W. Grimes 
14332c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1434df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1435df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1436df8bae1dSRodney W. Grimes 			return;
1437df8bae1dSRodney W. Grimes 		}
1438df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1439df8bae1dSRodney W. Grimes 	}
1440df8bae1dSRodney W. Grimes 
1441df8bae1dSRodney W. Grimes 	/*
1442df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1443df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1444df8bae1dSRodney W. Grimes 	 */
1445df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
1446df8bae1dSRodney W. Grimes 
1447df8bae1dSRodney W. Grimes 	/*
1448df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1449df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1450df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1451df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1452df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1453df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1454df8bae1dSRodney W. Grimes 	 */
1455df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1456df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1457df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1458df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1459df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1460df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1461df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1462df8bae1dSRodney W. Grimes 
1463df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1464df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1465df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1466df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1467df8bae1dSRodney W. Grimes 		    else
1468df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1469df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1470df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1471df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1472df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1473df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1474df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1475df8bae1dSRodney W. Grimes #endif
1476df8bae1dSRodney W. Grimes 		}
1477df8bae1dSRodney W. Grimes 	}
1478df8bae1dSRodney W. Grimes 
1479b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1480b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1481df8bae1dSRodney W. Grimes 	if (error)
1482df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1483df8bae1dSRodney W. Grimes 	else {
1484df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1485df8bae1dSRodney W. Grimes 		if (type)
1486df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1487df8bae1dSRodney W. Grimes 		else {
14881f91d8c5SDavid Greenman 			if (mcopy) {
14891f91d8c5SDavid Greenman 				ipflow_create(&ipforward_rt, mcopy);
1490df8bae1dSRodney W. Grimes 				m_freem(mcopy);
14911f91d8c5SDavid Greenman 			}
1492df8bae1dSRodney W. Grimes 			return;
1493df8bae1dSRodney W. Grimes 		}
1494df8bae1dSRodney W. Grimes 	}
1495df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1496df8bae1dSRodney W. Grimes 		return;
1497df8bae1dSRodney W. Grimes 	destifp = NULL;
1498df8bae1dSRodney W. Grimes 
1499df8bae1dSRodney W. Grimes 	switch (error) {
1500df8bae1dSRodney W. Grimes 
1501df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1502df8bae1dSRodney W. Grimes 		/* type, code set above */
1503df8bae1dSRodney W. Grimes 		break;
1504df8bae1dSRodney W. Grimes 
1505df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1506df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1507df8bae1dSRodney W. Grimes 	case ENETDOWN:
1508df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1509df8bae1dSRodney W. Grimes 	default:
1510df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1511df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1512df8bae1dSRodney W. Grimes 		break;
1513df8bae1dSRodney W. Grimes 
1514df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1515df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1516df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
1517df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1518df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
1519df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1520df8bae1dSRodney W. Grimes 		break;
1521df8bae1dSRodney W. Grimes 
1522df8bae1dSRodney W. Grimes 	case ENOBUFS:
1523df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1524df8bae1dSRodney W. Grimes 		code = 0;
1525df8bae1dSRodney W. Grimes 		break;
1526df8bae1dSRodney W. Grimes 	}
1527df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1528df8bae1dSRodney W. Grimes }
1529df8bae1dSRodney W. Grimes 
153082c23ebaSBill Fenner void
153182c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
153282c23ebaSBill Fenner 	register struct inpcb *inp;
153382c23ebaSBill Fenner 	register struct mbuf **mp;
153482c23ebaSBill Fenner 	register struct ip *ip;
153582c23ebaSBill Fenner 	register struct mbuf *m;
153682c23ebaSBill Fenner {
153782c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
153882c23ebaSBill Fenner 		struct timeval tv;
153982c23ebaSBill Fenner 
154082c23ebaSBill Fenner 		microtime(&tv);
154182c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
154282c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
154382c23ebaSBill Fenner 		if (*mp)
154482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
154582c23ebaSBill Fenner 	}
154682c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
154782c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
154882c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
154982c23ebaSBill Fenner 		if (*mp)
155082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
155182c23ebaSBill Fenner 	}
155282c23ebaSBill Fenner #ifdef notyet
155382c23ebaSBill Fenner 	/* XXX
155482c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
155582c23ebaSBill Fenner 	 * than they already were.
155682c23ebaSBill Fenner 	 */
155782c23ebaSBill Fenner 	/* options were tossed already */
155882c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
155982c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
156082c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
156182c23ebaSBill Fenner 		if (*mp)
156282c23ebaSBill Fenner 			mp = &(*mp)->m_next;
156382c23ebaSBill Fenner 	}
156482c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
156582c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
156682c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
156782c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
156882c23ebaSBill Fenner 		if (*mp)
156982c23ebaSBill Fenner 			mp = &(*mp)->m_next;
157082c23ebaSBill Fenner 	}
157182c23ebaSBill Fenner #endif
157282c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
1573d314ad7bSJulian Elischer 		struct ifnet *ifp;
1574d314ad7bSJulian Elischer 		struct sdlbuf {
157582c23ebaSBill Fenner 			struct sockaddr_dl sdl;
1576d314ad7bSJulian Elischer 			u_char	pad[32];
1577d314ad7bSJulian Elischer 		} sdlbuf;
1578d314ad7bSJulian Elischer 		struct sockaddr_dl *sdp;
1579d314ad7bSJulian Elischer 		struct sockaddr_dl *sdl2 = &sdlbuf.sdl;
158082c23ebaSBill Fenner 
1581d314ad7bSJulian Elischer 		if (((ifp = m->m_pkthdr.rcvif))
1582d314ad7bSJulian Elischer 		&& ( ifp->if_index && (ifp->if_index <= if_index))) {
1583d314ad7bSJulian Elischer 			sdp = (struct sockaddr_dl *)(ifnet_addrs
1584d314ad7bSJulian Elischer 					[ifp->if_index - 1]->ifa_addr);
1585d314ad7bSJulian Elischer 			/*
1586d314ad7bSJulian Elischer 			 * Change our mind and don't try copy.
1587d314ad7bSJulian Elischer 			 */
1588d314ad7bSJulian Elischer 			if ((sdp->sdl_family != AF_LINK)
1589d314ad7bSJulian Elischer 			|| (sdp->sdl_len > sizeof(sdlbuf))) {
1590d314ad7bSJulian Elischer 				goto makedummy;
1591d314ad7bSJulian Elischer 			}
1592d314ad7bSJulian Elischer 			bcopy(sdp, sdl2, sdp->sdl_len);
1593d314ad7bSJulian Elischer 		} else {
1594d314ad7bSJulian Elischer makedummy:
1595d314ad7bSJulian Elischer 			sdl2->sdl_len
1596d314ad7bSJulian Elischer 				= offsetof(struct sockaddr_dl, sdl_data[0]);
1597d314ad7bSJulian Elischer 			sdl2->sdl_family = AF_LINK;
1598d314ad7bSJulian Elischer 			sdl2->sdl_index = 0;
1599d314ad7bSJulian Elischer 			sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0;
1600d314ad7bSJulian Elischer 		}
1601d314ad7bSJulian Elischer 		*mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len,
160282c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
160382c23ebaSBill Fenner 		if (*mp)
160482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
160582c23ebaSBill Fenner 	}
160682c23ebaSBill Fenner }
160782c23ebaSBill Fenner 
1608df8bae1dSRodney W. Grimes int
1609f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1610f0068c4aSGarrett Wollman {
1611f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1612f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1613f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1614f0068c4aSGarrett Wollman 
1615f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1616f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1617f0068c4aSGarrett Wollman 
1618f0068c4aSGarrett Wollman 	ip_rsvpd = so;
16191c5de19aSGarrett Wollman 	/*
16201c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
16211c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
16221c5de19aSGarrett Wollman 	 */
16231c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
16241c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
16251c5de19aSGarrett Wollman 		rsvp_on++;
16261c5de19aSGarrett Wollman 	}
1627f0068c4aSGarrett Wollman 
1628f0068c4aSGarrett Wollman 	return 0;
1629f0068c4aSGarrett Wollman }
1630f0068c4aSGarrett Wollman 
1631f0068c4aSGarrett Wollman int
1632f0068c4aSGarrett Wollman ip_rsvp_done(void)
1633f0068c4aSGarrett Wollman {
1634f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
16351c5de19aSGarrett Wollman 	/*
16361c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
16371c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
16381c5de19aSGarrett Wollman 	 */
16391c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
16401c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
16411c5de19aSGarrett Wollman 		rsvp_on--;
16421c5de19aSGarrett Wollman 	}
1643f0068c4aSGarrett Wollman 	return 0;
1644f0068c4aSGarrett Wollman }
1645