xref: /freebsd/sys/netinet/ip_input.c (revision 194a213effd7c5d46ca63171a106c0325df67f74)
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
34194a213eSAndrey A. Chernov  * $Id: ip_input.c,v 1.64 1997/07/25 03:58:21 brian 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 
4074a9466cSGary Palmer #include "opt_ipfw.h"
4174a9466cSGary Palmer 
4282c23ebaSBill Fenner #include <stddef.h>
4382c23ebaSBill Fenner 
44df8bae1dSRodney W. Grimes #include <sys/param.h>
45df8bae1dSRodney W. Grimes #include <sys/systm.h>
46df8bae1dSRodney W. Grimes #include <sys/malloc.h>
47df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
48df8bae1dSRodney W. Grimes #include <sys/domain.h>
49df8bae1dSRodney W. Grimes #include <sys/protosw.h>
50df8bae1dSRodney W. Grimes #include <sys/socket.h>
51df8bae1dSRodney W. Grimes #include <sys/errno.h>
52df8bae1dSRodney W. Grimes #include <sys/time.h>
53df8bae1dSRodney W. Grimes #include <sys/kernel.h>
541025071fSGarrett Wollman #include <sys/syslog.h>
55b5e8ce9fSBruce Evans #include <sys/sysctl.h>
56df8bae1dSRodney W. Grimes 
57df8bae1dSRodney W. Grimes #include <net/if.h>
5882c23ebaSBill Fenner #include <net/if_dl.h>
59df8bae1dSRodney W. Grimes #include <net/route.h>
60748e0b0aSGarrett Wollman #include <net/netisr.h>
61df8bae1dSRodney W. Grimes 
62df8bae1dSRodney W. Grimes #include <netinet/in.h>
63df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
64b5e8ce9fSBruce Evans #include <netinet/in_var.h>
65df8bae1dSRodney W. Grimes #include <netinet/ip.h>
66df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
67df8bae1dSRodney W. Grimes #include <netinet/in_var.h>
68df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
69df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
7058938916SGarrett Wollman #include <machine/in_cksum.h>
71df8bae1dSRodney W. Grimes 
72f0068c4aSGarrett Wollman #include <sys/socketvar.h>
736ddbf1e2SGary Palmer 
746ddbf1e2SGary Palmer #ifdef IPFIREWALL
756ddbf1e2SGary Palmer #include <netinet/ip_fw.h>
766ddbf1e2SGary Palmer #endif
776ddbf1e2SGary Palmer 
781c5de19aSGarrett Wollman int rsvp_on = 0;
79f708ef1bSPoul-Henning Kamp static int ip_rsvp_on;
80f0068c4aSGarrett Wollman struct socket *ip_rsvpd;
81f0068c4aSGarrett Wollman 
82d4fb926cSGarrett Wollman static int	ipforwarding = 0;
830312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW,
840312fbe9SPoul-Henning Kamp 	&ipforwarding, 0, "");
850312fbe9SPoul-Henning Kamp 
86d4fb926cSGarrett Wollman static int	ipsendredirects = 1; /* XXX */
870312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW,
880312fbe9SPoul-Henning Kamp 	&ipsendredirects, 0, "");
890312fbe9SPoul-Henning Kamp 
90df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
910312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW,
920312fbe9SPoul-Henning Kamp 	&ip_defttl, 0, "");
930312fbe9SPoul-Henning Kamp 
940312fbe9SPoul-Henning Kamp static int	ip_dosourceroute = 0;
950312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
960312fbe9SPoul-Henning Kamp 	&ip_dosourceroute, 0, "");
97df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
980312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
99df8bae1dSRodney W. Grimes #endif
100df8bae1dSRodney W. Grimes 
101df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
102df8bae1dSRodney W. Grimes extern	struct protosw inetsw[];
103df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
1040312fbe9SPoul-Henning Kamp static int	ipqmaxlen = IFQ_MAXLEN;
10559562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; /* first inet address */
106df8bae1dSRodney W. Grimes struct	ifqueue ipintrq;
1070312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RD,
1080312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_maxlen, 0, "");
1090312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1100312fbe9SPoul-Henning Kamp 	&ipintrq.ifq_drops, 0, "");
111df8bae1dSRodney W. Grimes 
112f23b4c91SGarrett Wollman struct ipstat ipstat;
113194a213eSAndrey A. Chernov 
114194a213eSAndrey A. Chernov /* Packet reassembly stuff */
115194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
116194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
117194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
118194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
119194a213eSAndrey A. Chernov 	((((x) & 0xF | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
120194a213eSAndrey A. Chernov 
121194a213eSAndrey A. Chernov static struct ipq ipq[IPREASS_NHASH];
122194a213eSAndrey A. Chernov static int    nipq = 0;         /* total # of reass queues */
123194a213eSAndrey A. Chernov static int    maxnipq;
124f23b4c91SGarrett Wollman 
1250312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1260312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1270312fbe9SPoul-Henning Kamp 	&ip_mtu, 0, "");
1280312fbe9SPoul-Henning Kamp #endif
1290312fbe9SPoul-Henning Kamp 
13058938916SGarrett Wollman #if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1
13158938916SGarrett Wollman #undef COMPAT_IPFW
13258938916SGarrett Wollman #define COMPAT_IPFW 1
13358938916SGarrett Wollman #else
13458938916SGarrett Wollman #undef COMPAT_IPFW
13558938916SGarrett Wollman #endif
13658938916SGarrett Wollman 
13758938916SGarrett Wollman #ifdef COMPAT_IPFW
13823bf9953SPoul-Henning Kamp /* Firewall hooks */
13923bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
14023bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
141e7319babSPoul-Henning Kamp 
142fed1c7e9SSøren Schmidt /* IP Network Address Translation (NAT) hooks */
143fed1c7e9SSøren Schmidt ip_nat_t *ip_nat_ptr;
144fed1c7e9SSøren Schmidt ip_nat_ctl_t *ip_nat_ctl_ptr;
14558938916SGarrett Wollman #endif
146fed1c7e9SSøren Schmidt 
147afed1b49SDarren Reed #if defined(IPFILTER_LKM) || defined(IPFILTER)
148afed1b49SDarren Reed int fr_check __P((struct ip *, int, struct ifnet *, int, struct mbuf **));
149afed1b49SDarren Reed int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)) = NULL;
150afed1b49SDarren Reed #endif
151afed1b49SDarren Reed 
152afed1b49SDarren Reed 
153e7319babSPoul-Henning Kamp /*
154df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
155df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
156df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
157df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
158df8bae1dSRodney W. Grimes  * to us.
159df8bae1dSRodney W. Grimes  */
1600312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
161df8bae1dSRodney W. Grimes static	struct ip_srcrt {
162df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
163df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
164df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
165df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
166df8bae1dSRodney W. Grimes } ip_srcrt;
167df8bae1dSRodney W. Grimes 
16893e0e116SJulian Elischer #ifdef IPDIVERT
16993e0e116SJulian Elischer /*
17093e0e116SJulian Elischer  * Shared variable between ip_input() and ip_reass() to communicate
17193e0e116SJulian Elischer  * about which packets, once assembled from fragments, get diverted,
17293e0e116SJulian Elischer  * and to which port.
17393e0e116SJulian Elischer  */
17493e0e116SJulian Elischer static u_short	frag_divert_port;
17593e0e116SJulian Elischer #endif
17693e0e116SJulian Elischer 
177df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr));
1780312fbe9SPoul-Henning Kamp static void	 ip_deq __P((struct ipasfrag *));
1790312fbe9SPoul-Henning Kamp static int	 ip_dooptions __P((struct mbuf *));
1800312fbe9SPoul-Henning Kamp static void	 ip_enq __P((struct ipasfrag *, struct ipasfrag *));
1810312fbe9SPoul-Henning Kamp static void	 ip_forward __P((struct mbuf *, int));
1820312fbe9SPoul-Henning Kamp static void	 ip_freef __P((struct ipq *));
1830312fbe9SPoul-Henning Kamp static struct ip *
184194a213eSAndrey A. Chernov 	 ip_reass __P((struct ipasfrag *, struct ipq *, struct ipq *));
1850312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1860312fbe9SPoul-Henning Kamp 	 ip_rtaddr __P((struct in_addr));
1870312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
188df8bae1dSRodney W. Grimes /*
189df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
190df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
191df8bae1dSRodney W. Grimes  */
192df8bae1dSRodney W. Grimes void
193df8bae1dSRodney W. Grimes ip_init()
194df8bae1dSRodney W. Grimes {
195df8bae1dSRodney W. Grimes 	register struct protosw *pr;
196df8bae1dSRodney W. Grimes 	register int i;
197df8bae1dSRodney W. Grimes 
19859562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
199df8bae1dSRodney W. Grimes 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
200df8bae1dSRodney W. Grimes 	if (pr == 0)
201df8bae1dSRodney W. Grimes 		panic("ip_init");
202df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
203df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
204df8bae1dSRodney W. Grimes 	for (pr = inetdomain.dom_protosw;
205df8bae1dSRodney W. Grimes 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
206df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
207df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
208df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
209194a213eSAndrey A. Chernov 
210194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
211194a213eSAndrey A. Chernov 	    ipq[i].next = ipq[i].prev = &ipq[i];
212194a213eSAndrey A. Chernov 
213194a213eSAndrey A. Chernov 	maxnipq = nmbclusters/4;
214194a213eSAndrey A. Chernov 
215df8bae1dSRodney W. Grimes 	ip_id = time.tv_sec & 0xffff;
216df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
217b83e4314SPoul-Henning Kamp #ifdef IPFIREWALL
218b83e4314SPoul-Henning Kamp 	ip_fw_init();
219b83e4314SPoul-Henning Kamp #endif
220fed1c7e9SSøren Schmidt #ifdef IPNAT
221fed1c7e9SSøren Schmidt         ip_nat_init();
222fed1c7e9SSøren Schmidt #endif
223fed1c7e9SSøren Schmidt 
224df8bae1dSRodney W. Grimes }
225df8bae1dSRodney W. Grimes 
2260312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
227f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
228df8bae1dSRodney W. Grimes 
229df8bae1dSRodney W. Grimes /*
230df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
231df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
232df8bae1dSRodney W. Grimes  */
233c67b1d17SGarrett Wollman void
234c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
235df8bae1dSRodney W. Grimes {
23623bf9953SPoul-Henning Kamp 	struct ip *ip;
23723bf9953SPoul-Henning Kamp 	struct ipq *fp;
23823bf9953SPoul-Henning Kamp 	struct in_ifaddr *ia;
239194a213eSAndrey A. Chernov 	int    i, hlen;
24047c861ecSBrian Somers 	u_short sum;
241df8bae1dSRodney W. Grimes 
242df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
243df8bae1dSRodney W. Grimes 	if ((m->m_flags & M_PKTHDR) == 0)
24458938916SGarrett Wollman 		panic("ip_input no HDR");
245df8bae1dSRodney W. Grimes #endif
246df8bae1dSRodney W. Grimes 	/*
247df8bae1dSRodney W. Grimes 	 * If no IP addresses have been set yet but the interfaces
248df8bae1dSRodney W. Grimes 	 * are receiving, can't do anything with incoming packets yet.
24959562606SGarrett Wollman 	 * XXX This is broken! We should be able to receive broadcasts
25059562606SGarrett Wollman 	 * and multicasts even without any local addresses configured.
251df8bae1dSRodney W. Grimes 	 */
25259562606SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead))
253df8bae1dSRodney W. Grimes 		goto bad;
254df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
25558938916SGarrett Wollman 
25658938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
25758938916SGarrett Wollman 		goto tooshort;
25858938916SGarrett Wollman 
25958938916SGarrett Wollman #ifdef	DIAGNOSTIC
26058938916SGarrett Wollman 	if (m->m_len < sizeof(struct ip))
26158938916SGarrett Wollman 		panic("ipintr mbuf too short");
26258938916SGarrett Wollman #endif
26358938916SGarrett Wollman 
264df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
265df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
266df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
267c67b1d17SGarrett Wollman 		return;
268df8bae1dSRodney W. Grimes 	}
269df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
27058938916SGarrett Wollman 
27158938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
272df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
273df8bae1dSRodney W. Grimes 		goto bad;
274df8bae1dSRodney W. Grimes 	}
27558938916SGarrett Wollman 
27658938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
277df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
278df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
279df8bae1dSRodney W. Grimes 		goto bad;
280df8bae1dSRodney W. Grimes 	}
281df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
282df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
283df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
284c67b1d17SGarrett Wollman 			return;
285df8bae1dSRodney W. Grimes 		}
286df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
287df8bae1dSRodney W. Grimes 	}
28858938916SGarrett Wollman 	if (hlen == sizeof(struct ip)) {
28947c861ecSBrian Somers 		sum = in_cksum_hdr(ip);
29058938916SGarrett Wollman 	} else {
29147c861ecSBrian Somers 		sum = in_cksum(m, hlen);
29258938916SGarrett Wollman 	}
29347c861ecSBrian Somers 	if (sum) {
294df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
295df8bae1dSRodney W. Grimes 		goto bad;
296df8bae1dSRodney W. Grimes 	}
297df8bae1dSRodney W. Grimes 
298df8bae1dSRodney W. Grimes 	/*
299df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
300df8bae1dSRodney W. Grimes 	 */
301df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
302df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
303df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
304df8bae1dSRodney W. Grimes 		goto bad;
305df8bae1dSRodney W. Grimes 	}
306df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_id);
307df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
308df8bae1dSRodney W. Grimes 
309df8bae1dSRodney W. Grimes 	/*
310df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
311df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
312df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
313df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
314df8bae1dSRodney W. Grimes 	 */
315df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
31658938916SGarrett Wollman tooshort:
317df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
318df8bae1dSRodney W. Grimes 		goto bad;
319df8bae1dSRodney W. Grimes 	}
320df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
321df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
322df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
323df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
324df8bae1dSRodney W. Grimes 		} else
325df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
326df8bae1dSRodney W. Grimes 	}
3274dd1662bSUgen J.S. Antsilevich 	/*
3284dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3294dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
3304dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
3314dd1662bSUgen J.S. Antsilevich 	 * deals with it.
33293e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
333fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
3344dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
3354dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
3364dd1662bSUgen J.S. Antsilevich  	 */
337beec8214SDarren Reed #if defined(IPFILTER) || defined(IPFILTER_LKM)
338beec8214SDarren Reed 	/*
339beec8214SDarren Reed 	 * Check if we want to allow this packet to be processed.
340beec8214SDarren Reed 	 * Consider it to be bad if not.
341beec8214SDarren Reed 	 */
342beec8214SDarren Reed 	if (fr_check) {
343beec8214SDarren Reed 		struct	mbuf	*m1 = m;
344df8bae1dSRodney W. Grimes 
345beec8214SDarren Reed 		if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m1) || !m1)
346beec8214SDarren Reed 			return;
347beec8214SDarren Reed 		ip = mtod(m = m1, struct ip *);
348beec8214SDarren Reed 	}
349beec8214SDarren Reed #endif
35058938916SGarrett Wollman #ifdef COMPAT_IPFW
35193e0e116SJulian Elischer 	if (ip_fw_chk_ptr) {
352e4676ba6SJulian Elischer #ifdef IPDIVERT
353e4676ba6SJulian Elischer 		u_short port;
35493e0e116SJulian Elischer 
355e4676ba6SJulian Elischer 		port = (*ip_fw_chk_ptr)(&ip, hlen, NULL, ip_divert_ignore, &m);
356d81e4043SBrian Somers 		ip_divert_ignore = 0;
357e4676ba6SJulian Elischer 		if (port) {			/* Divert packet */
358e4676ba6SJulian Elischer 			frag_divert_port = port;
35993e0e116SJulian Elischer 			goto ours;
36093e0e116SJulian Elischer 		}
361e4676ba6SJulian Elischer #else
362e4676ba6SJulian Elischer 		/* If ipfw says divert, we have to just drop packet */
363e4676ba6SJulian Elischer 		if ((*ip_fw_chk_ptr)(&ip, hlen, NULL, 0, &m)) {
364e4676ba6SJulian Elischer 			m_freem(m);
365e4676ba6SJulian Elischer 			m = NULL;
366e4676ba6SJulian Elischer 		}
367e4676ba6SJulian Elischer #endif
368e4676ba6SJulian Elischer 		if (!m)
369e4676ba6SJulian Elischer 			return;
37093e0e116SJulian Elischer 	}
371100ba1a6SJordan K. Hubbard 
3726713d4a7SSøren Schmidt         if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN))
373fed1c7e9SSøren Schmidt 		return;
37458938916SGarrett Wollman #endif
375fed1c7e9SSøren Schmidt 
376df8bae1dSRodney W. Grimes 	/*
377df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
378df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
379df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
380df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
381df8bae1dSRodney W. Grimes 	 */
382df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
383df8bae1dSRodney W. Grimes 	if (hlen > sizeof (struct ip) && ip_dooptions(m))
384c67b1d17SGarrett Wollman 		return;
385df8bae1dSRodney W. Grimes 
386f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
387f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
388f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
389f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
390f0068c4aSGarrett Wollman 	 * grabbing the packet.
391f0068c4aSGarrett Wollman          */
3921c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
393f0068c4aSGarrett Wollman 		goto ours;
394f0068c4aSGarrett Wollman 
395df8bae1dSRodney W. Grimes 	/*
396df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
397df8bae1dSRodney W. Grimes 	 */
39859562606SGarrett Wollman 	for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
399df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
400df8bae1dSRodney W. Grimes 
401df8bae1dSRodney W. Grimes 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
402df8bae1dSRodney W. Grimes 			goto ours;
403432aad0eSTor Egge #ifdef BOOTP_COMPAT
404432aad0eSTor Egge 		if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
405432aad0eSTor Egge 			goto ours;
406432aad0eSTor Egge #endif
4076ed666afSPoul-Henning Kamp 		if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
408df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
409df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr)
410df8bae1dSRodney W. Grimes 				goto ours;
411df8bae1dSRodney W. Grimes 			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
412df8bae1dSRodney W. Grimes 				goto ours;
413df8bae1dSRodney W. Grimes 		}
414df8bae1dSRodney W. Grimes 	}
415df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
416df8bae1dSRodney W. Grimes 		struct in_multi *inm;
417df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
418df8bae1dSRodney W. Grimes 			/*
419df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
420df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
421df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
422df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
423df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
424df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
425df8bae1dSRodney W. Grimes 			 *
426df8bae1dSRodney W. Grimes 			 * (The IP ident field is put in the same byte order
427df8bae1dSRodney W. Grimes 			 * as expected when ip_mforward() is called from
428df8bae1dSRodney W. Grimes 			 * ip_output().)
429df8bae1dSRodney W. Grimes 			 */
430df8bae1dSRodney W. Grimes 			ip->ip_id = htons(ip->ip_id);
431f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
432df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
433df8bae1dSRodney W. Grimes 				m_freem(m);
434c67b1d17SGarrett Wollman 				return;
435df8bae1dSRodney W. Grimes 			}
436df8bae1dSRodney W. Grimes 			ip->ip_id = ntohs(ip->ip_id);
437df8bae1dSRodney W. Grimes 
438df8bae1dSRodney W. Grimes 			/*
439df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
440df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
441df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
442df8bae1dSRodney W. Grimes 			 */
443df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
444df8bae1dSRodney W. Grimes 				goto ours;
445df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
446df8bae1dSRodney W. Grimes 		}
447df8bae1dSRodney W. Grimes 		/*
448df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
449df8bae1dSRodney W. Grimes 		 * arrival interface.
450df8bae1dSRodney W. Grimes 		 */
451df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
452df8bae1dSRodney W. Grimes 		if (inm == NULL) {
45382c39223SGarrett Wollman 			ipstat.ips_notmember++;
454df8bae1dSRodney W. Grimes 			m_freem(m);
455c67b1d17SGarrett Wollman 			return;
456df8bae1dSRodney W. Grimes 		}
457df8bae1dSRodney W. Grimes 		goto ours;
458df8bae1dSRodney W. Grimes 	}
459df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
460df8bae1dSRodney W. Grimes 		goto ours;
461df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
462df8bae1dSRodney W. Grimes 		goto ours;
463df8bae1dSRodney W. Grimes 
464df8bae1dSRodney W. Grimes 	/*
465df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
466df8bae1dSRodney W. Grimes 	 */
467df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
468df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
469df8bae1dSRodney W. Grimes 		m_freem(m);
470df8bae1dSRodney W. Grimes 	} else
471df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
472c67b1d17SGarrett Wollman 	return;
473df8bae1dSRodney W. Grimes 
474df8bae1dSRodney W. Grimes ours:
475100ba1a6SJordan K. Hubbard 
47663f8d699SJordan K. Hubbard 	/*
477df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
478df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
479df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
480df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
481df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
482df8bae1dSRodney W. Grimes 	 */
483c7a6ccb3SDavid Greenman 	if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
484df8bae1dSRodney W. Grimes 		if (m->m_flags & M_EXT) {		/* XXX */
485df8bae1dSRodney W. Grimes 			if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
486df8bae1dSRodney W. Grimes 				ipstat.ips_toosmall++;
48793e0e116SJulian Elischer #ifdef IPDIVERT
48893e0e116SJulian Elischer 				frag_divert_port = 0;
48993e0e116SJulian Elischer #endif
490c67b1d17SGarrett Wollman 				return;
491df8bae1dSRodney W. Grimes 			}
492df8bae1dSRodney W. Grimes 			ip = mtod(m, struct ip *);
493df8bae1dSRodney W. Grimes 		}
494194a213eSAndrey A. Chernov 		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
495df8bae1dSRodney W. Grimes 		/*
496df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
497df8bae1dSRodney W. Grimes 		 * of this datagram.
498df8bae1dSRodney W. Grimes 		 */
499194a213eSAndrey A. Chernov 		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
500df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
501df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
502df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
503df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
504df8bae1dSRodney W. Grimes 				goto found;
505df8bae1dSRodney W. Grimes 
506194a213eSAndrey A. Chernov 		fp = 0;
507194a213eSAndrey A. Chernov 
508194a213eSAndrey A. Chernov 		/* check if there's a place for the new queue */
509194a213eSAndrey A. Chernov 		if (nipq > maxnipq) {
510194a213eSAndrey A. Chernov 		    /*
511194a213eSAndrey A. Chernov 		     * drop something from the tail of the current queue
512194a213eSAndrey A. Chernov 		     * before proceeding further
513194a213eSAndrey A. Chernov 		     */
514194a213eSAndrey A. Chernov 		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */
515194a213eSAndrey A. Chernov 			for (i = 0; i < IPREASS_NHASH; i++) {
516194a213eSAndrey A. Chernov 			    if (ipq[i].prev != &ipq[i]) {
517194a213eSAndrey A. Chernov 				ip_freef(ipq[i].prev);
518194a213eSAndrey A. Chernov 				break;
519194a213eSAndrey A. Chernov 			    }
520194a213eSAndrey A. Chernov 			}
521194a213eSAndrey A. Chernov 		    } else
522194a213eSAndrey A. Chernov 			ip_freef(ipq[sum].prev);
523194a213eSAndrey A. Chernov 		}
524194a213eSAndrey A. Chernov found:
525df8bae1dSRodney W. Grimes 		/*
526df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
527df8bae1dSRodney W. Grimes 		 * set ip_mff if more fragments are expected,
528df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
529df8bae1dSRodney W. Grimes 		 */
530df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
531df8bae1dSRodney W. Grimes 		((struct ipasfrag *)ip)->ipf_mff &= ~1;
532df8bae1dSRodney W. Grimes 		if (ip->ip_off & IP_MF)
533df8bae1dSRodney W. Grimes 			((struct ipasfrag *)ip)->ipf_mff |= 1;
534df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
535df8bae1dSRodney W. Grimes 
536df8bae1dSRodney W. Grimes 		/*
537df8bae1dSRodney W. Grimes 		 * If datagram marked as having more fragments
538df8bae1dSRodney W. Grimes 		 * or if this is not the first fragment,
539df8bae1dSRodney W. Grimes 		 * attempt reassembly; if it succeeds, proceed.
540df8bae1dSRodney W. Grimes 		 */
541df8bae1dSRodney W. Grimes 		if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
542df8bae1dSRodney W. Grimes 			ipstat.ips_fragments++;
543194a213eSAndrey A. Chernov 			ip = ip_reass((struct ipasfrag *)ip, fp, &ipq[sum]);
544df8bae1dSRodney W. Grimes 			if (ip == 0)
545c67b1d17SGarrett Wollman 				return;
546df8bae1dSRodney W. Grimes 			ipstat.ips_reassembled++;
547df8bae1dSRodney W. Grimes 			m = dtom(ip);
548af782f1cSBrian Somers #ifdef IPDIVERT
549af782f1cSBrian Somers 			if (frag_divert_port) {
550af782f1cSBrian Somers 				ip->ip_len += hlen;
551af782f1cSBrian Somers 				HTONS(ip->ip_len);
552af782f1cSBrian Somers 				HTONS(ip->ip_off);
553af782f1cSBrian Somers 				HTONS(ip->ip_id);
554af782f1cSBrian Somers 				ip->ip_sum = 0;
555af782f1cSBrian Somers 				ip->ip_sum = in_cksum_hdr(ip);
556af782f1cSBrian Somers 				NTOHS(ip->ip_id);
557af782f1cSBrian Somers 				NTOHS(ip->ip_off);
558af782f1cSBrian Somers 				NTOHS(ip->ip_len);
559af782f1cSBrian Somers 				ip->ip_len -= hlen;
560af782f1cSBrian Somers 			}
561af782f1cSBrian Somers #endif
562df8bae1dSRodney W. Grimes 		} else
563df8bae1dSRodney W. Grimes 			if (fp)
564df8bae1dSRodney W. Grimes 				ip_freef(fp);
565df8bae1dSRodney W. Grimes 	} else
566df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
567df8bae1dSRodney W. Grimes 
56893e0e116SJulian Elischer #ifdef IPDIVERT
56993e0e116SJulian Elischer 	/*
570e4676ba6SJulian Elischer 	 * Divert reassembled packets to the divert protocol if required
57193e0e116SJulian Elischer 	 */
57293e0e116SJulian Elischer 	if (frag_divert_port) {
573e4676ba6SJulian Elischer 		ipstat.ips_delivered++;
57493e0e116SJulian Elischer 		ip_divert_port = frag_divert_port;
57593e0e116SJulian Elischer 		frag_divert_port = 0;
57693e0e116SJulian Elischer 		(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);
57793e0e116SJulian Elischer 		return;
57893e0e116SJulian Elischer 	}
57993e0e116SJulian Elischer #endif
58093e0e116SJulian Elischer 
581df8bae1dSRodney W. Grimes 	/*
582df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
583df8bae1dSRodney W. Grimes 	 */
584df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
585df8bae1dSRodney W. Grimes 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
586c67b1d17SGarrett Wollman 	return;
587df8bae1dSRodney W. Grimes bad:
588df8bae1dSRodney W. Grimes 	m_freem(m);
589c67b1d17SGarrett Wollman }
590c67b1d17SGarrett Wollman 
591c67b1d17SGarrett Wollman /*
592c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
593c67b1d17SGarrett Wollman  */
594c67b1d17SGarrett Wollman static void
595c67b1d17SGarrett Wollman ipintr(void)
596c67b1d17SGarrett Wollman {
597c67b1d17SGarrett Wollman 	int s;
598c67b1d17SGarrett Wollman 	struct mbuf *m;
599c67b1d17SGarrett Wollman 
600c67b1d17SGarrett Wollman 	while(1) {
601c67b1d17SGarrett Wollman 		s = splimp();
602c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
603c67b1d17SGarrett Wollman 		splx(s);
604c67b1d17SGarrett Wollman 		if (m == 0)
605c67b1d17SGarrett Wollman 			return;
606c67b1d17SGarrett Wollman 		ip_input(m);
607c67b1d17SGarrett Wollman 	}
608df8bae1dSRodney W. Grimes }
609df8bae1dSRodney W. Grimes 
610748e0b0aSGarrett Wollman NETISR_SET(NETISR_IP, ipintr);
611748e0b0aSGarrett Wollman 
612df8bae1dSRodney W. Grimes /*
613df8bae1dSRodney W. Grimes  * Take incoming datagram fragment and try to
614df8bae1dSRodney W. Grimes  * reassemble it into whole datagram.  If a chain for
615df8bae1dSRodney W. Grimes  * reassembly of this datagram already exists, then it
616df8bae1dSRodney W. Grimes  * is given as fp; otherwise have to make a chain.
617df8bae1dSRodney W. Grimes  */
6180312fbe9SPoul-Henning Kamp static struct ip *
619194a213eSAndrey A. Chernov ip_reass(ip, fp, where)
620df8bae1dSRodney W. Grimes 	register struct ipasfrag *ip;
621df8bae1dSRodney W. Grimes 	register struct ipq *fp;
622194a213eSAndrey A. Chernov 	struct   ipq    *where;
623df8bae1dSRodney W. Grimes {
624df8bae1dSRodney W. Grimes 	register struct mbuf *m = dtom(ip);
625df8bae1dSRodney W. Grimes 	register struct ipasfrag *q;
626df8bae1dSRodney W. Grimes 	struct mbuf *t;
627df8bae1dSRodney W. Grimes 	int hlen = ip->ip_hl << 2;
628df8bae1dSRodney W. Grimes 	int i, next;
629df8bae1dSRodney W. Grimes 
630df8bae1dSRodney W. Grimes 	/*
631df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
632df8bae1dSRodney W. Grimes 	 * would confuse code below.
633df8bae1dSRodney W. Grimes 	 */
634df8bae1dSRodney W. Grimes 	m->m_data += hlen;
635df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
636df8bae1dSRodney W. Grimes 
637df8bae1dSRodney W. Grimes 	/*
638df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
639df8bae1dSRodney W. Grimes 	 */
640df8bae1dSRodney W. Grimes 	if (fp == 0) {
641df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
642df8bae1dSRodney W. Grimes 			goto dropfrag;
643df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
644194a213eSAndrey A. Chernov 		insque(fp, where);
645194a213eSAndrey A. Chernov 		nipq++;
646df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
647df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
648df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
649df8bae1dSRodney W. Grimes 		fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
650df8bae1dSRodney W. Grimes 		fp->ipq_src = ((struct ip *)ip)->ip_src;
651df8bae1dSRodney W. Grimes 		fp->ipq_dst = ((struct ip *)ip)->ip_dst;
65293e0e116SJulian Elischer #ifdef IPDIVERT
65393e0e116SJulian Elischer 		fp->ipq_divert = 0;
65493e0e116SJulian Elischer #endif
655df8bae1dSRodney W. Grimes 		q = (struct ipasfrag *)fp;
656df8bae1dSRodney W. Grimes 		goto insert;
657df8bae1dSRodney W. Grimes 	}
658df8bae1dSRodney W. Grimes 
659df8bae1dSRodney W. Grimes 	/*
660df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
661df8bae1dSRodney W. Grimes 	 */
662df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
663df8bae1dSRodney W. Grimes 		if (q->ip_off > ip->ip_off)
664df8bae1dSRodney W. Grimes 			break;
665df8bae1dSRodney W. Grimes 
666df8bae1dSRodney W. Grimes 	/*
667df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
668df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
669df8bae1dSRodney W. Grimes 	 * segment.  If it provides all of our data, drop us.
670df8bae1dSRodney W. Grimes 	 */
671df8bae1dSRodney W. Grimes 	if (q->ipf_prev != (struct ipasfrag *)fp) {
672df8bae1dSRodney W. Grimes 		i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
673df8bae1dSRodney W. Grimes 		if (i > 0) {
674df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
675df8bae1dSRodney W. Grimes 				goto dropfrag;
676df8bae1dSRodney W. Grimes 			m_adj(dtom(ip), i);
677df8bae1dSRodney W. Grimes 			ip->ip_off += i;
678df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
679df8bae1dSRodney W. Grimes 		}
680df8bae1dSRodney W. Grimes 	}
681df8bae1dSRodney W. Grimes 
682df8bae1dSRodney W. Grimes 	/*
683df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
684df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
685df8bae1dSRodney W. Grimes 	 */
686df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
687e7c81944SDavid Greenman 		struct mbuf *m0;
688e7c81944SDavid Greenman 
689df8bae1dSRodney W. Grimes 		i = (ip->ip_off + ip->ip_len) - q->ip_off;
690df8bae1dSRodney W. Grimes 		if (i < q->ip_len) {
691df8bae1dSRodney W. Grimes 			q->ip_len -= i;
692df8bae1dSRodney W. Grimes 			q->ip_off += i;
693df8bae1dSRodney W. Grimes 			m_adj(dtom(q), i);
694df8bae1dSRodney W. Grimes 			break;
695df8bae1dSRodney W. Grimes 		}
696e7c81944SDavid Greenman 		m0 = dtom(q);
697df8bae1dSRodney W. Grimes 		q = q->ipf_next;
698df8bae1dSRodney W. Grimes 		ip_deq(q->ipf_prev);
699e7c81944SDavid Greenman 		m_freem(m0);
700df8bae1dSRodney W. Grimes 	}
701df8bae1dSRodney W. Grimes 
702df8bae1dSRodney W. Grimes insert:
70393e0e116SJulian Elischer 
70493e0e116SJulian Elischer #ifdef IPDIVERT
70593e0e116SJulian Elischer 	/*
70693e0e116SJulian Elischer 	 * Any fragment diverting causes the whole packet to divert
70793e0e116SJulian Elischer 	 */
70893e0e116SJulian Elischer 	if (frag_divert_port != 0)
70993e0e116SJulian Elischer 		fp->ipq_divert = frag_divert_port;
71093e0e116SJulian Elischer 	frag_divert_port = 0;
71193e0e116SJulian Elischer #endif
71293e0e116SJulian Elischer 
713df8bae1dSRodney W. Grimes 	/*
714df8bae1dSRodney W. Grimes 	 * Stick new segment in its place;
715df8bae1dSRodney W. Grimes 	 * check for complete reassembly.
716df8bae1dSRodney W. Grimes 	 */
717df8bae1dSRodney W. Grimes 	ip_enq(ip, q->ipf_prev);
718df8bae1dSRodney W. Grimes 	next = 0;
719df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
720df8bae1dSRodney W. Grimes 		if (q->ip_off != next)
721df8bae1dSRodney W. Grimes 			return (0);
722df8bae1dSRodney W. Grimes 		next += q->ip_len;
723df8bae1dSRodney W. Grimes 	}
724df8bae1dSRodney W. Grimes 	if (q->ipf_prev->ipf_mff & 1)
725df8bae1dSRodney W. Grimes 		return (0);
726df8bae1dSRodney W. Grimes 
727df8bae1dSRodney W. Grimes 	/*
728430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
729430d30d8SBill Fenner 	 */
730430d30d8SBill Fenner 	if (next + (IP_VHL_HL(((struct ip *)fp->ipq_next)->ip_vhl) << 2)
731430d30d8SBill Fenner 							> IP_MAXPACKET) {
732430d30d8SBill Fenner 		ipstat.ips_toolong++;
733430d30d8SBill Fenner 		ip_freef(fp);
734430d30d8SBill Fenner 		return (0);
735430d30d8SBill Fenner 	}
736430d30d8SBill Fenner 
737430d30d8SBill Fenner 	/*
738430d30d8SBill Fenner 	 * Concatenate fragments.
739df8bae1dSRodney W. Grimes 	 */
740df8bae1dSRodney W. Grimes 	q = fp->ipq_next;
741df8bae1dSRodney W. Grimes 	m = dtom(q);
742df8bae1dSRodney W. Grimes 	t = m->m_next;
743df8bae1dSRodney W. Grimes 	m->m_next = 0;
744df8bae1dSRodney W. Grimes 	m_cat(m, t);
745df8bae1dSRodney W. Grimes 	q = q->ipf_next;
746df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp) {
747df8bae1dSRodney W. Grimes 		t = dtom(q);
748df8bae1dSRodney W. Grimes 		q = q->ipf_next;
749df8bae1dSRodney W. Grimes 		m_cat(m, t);
750df8bae1dSRodney W. Grimes 	}
751df8bae1dSRodney W. Grimes 
75293e0e116SJulian Elischer #ifdef IPDIVERT
75393e0e116SJulian Elischer 	/*
75493e0e116SJulian Elischer 	 * Record divert port for packet, if any
75593e0e116SJulian Elischer 	 */
75693e0e116SJulian Elischer 	frag_divert_port = fp->ipq_divert;
75793e0e116SJulian Elischer #endif
75893e0e116SJulian Elischer 
759df8bae1dSRodney W. Grimes 	/*
760df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
761df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
762df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
763df8bae1dSRodney W. Grimes 	 * Make header visible.
764df8bae1dSRodney W. Grimes 	 */
765df8bae1dSRodney W. Grimes 	ip = fp->ipq_next;
766df8bae1dSRodney W. Grimes 	ip->ip_len = next;
767df8bae1dSRodney W. Grimes 	ip->ipf_mff &= ~1;
768df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_src = fp->ipq_src;
769df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_dst = fp->ipq_dst;
770df8bae1dSRodney W. Grimes 	remque(fp);
771194a213eSAndrey A. Chernov 	nipq--;
772df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
773df8bae1dSRodney W. Grimes 	m = dtom(ip);
774df8bae1dSRodney W. Grimes 	m->m_len += (ip->ip_hl << 2);
775df8bae1dSRodney W. Grimes 	m->m_data -= (ip->ip_hl << 2);
776df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
777df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
778df8bae1dSRodney W. Grimes 		register int plen = 0;
779df8bae1dSRodney W. Grimes 		for (t = m; m; m = m->m_next)
780df8bae1dSRodney W. Grimes 			plen += m->m_len;
781df8bae1dSRodney W. Grimes 		t->m_pkthdr.len = plen;
782df8bae1dSRodney W. Grimes 	}
783df8bae1dSRodney W. Grimes 	return ((struct ip *)ip);
784df8bae1dSRodney W. Grimes 
785df8bae1dSRodney W. Grimes dropfrag:
786df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
787df8bae1dSRodney W. Grimes 	m_freem(m);
788df8bae1dSRodney W. Grimes 	return (0);
789df8bae1dSRodney W. Grimes }
790df8bae1dSRodney W. Grimes 
791df8bae1dSRodney W. Grimes /*
792df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
793df8bae1dSRodney W. Grimes  * associated datagrams.
794df8bae1dSRodney W. Grimes  */
7950312fbe9SPoul-Henning Kamp static void
796df8bae1dSRodney W. Grimes ip_freef(fp)
797df8bae1dSRodney W. Grimes 	struct ipq *fp;
798df8bae1dSRodney W. Grimes {
799df8bae1dSRodney W. Grimes 	register struct ipasfrag *q, *p;
800df8bae1dSRodney W. Grimes 
801df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
802df8bae1dSRodney W. Grimes 		p = q->ipf_next;
803df8bae1dSRodney W. Grimes 		ip_deq(q);
804df8bae1dSRodney W. Grimes 		m_freem(dtom(q));
805df8bae1dSRodney W. Grimes 	}
806df8bae1dSRodney W. Grimes 	remque(fp);
807df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
808194a213eSAndrey A. Chernov 	nipq--;
809df8bae1dSRodney W. Grimes }
810df8bae1dSRodney W. Grimes 
811df8bae1dSRodney W. Grimes /*
812df8bae1dSRodney W. Grimes  * Put an ip fragment on a reassembly chain.
813df8bae1dSRodney W. Grimes  * Like insque, but pointers in middle of structure.
814df8bae1dSRodney W. Grimes  */
8150312fbe9SPoul-Henning Kamp static void
816df8bae1dSRodney W. Grimes ip_enq(p, prev)
817df8bae1dSRodney W. Grimes 	register struct ipasfrag *p, *prev;
818df8bae1dSRodney W. Grimes {
819df8bae1dSRodney W. Grimes 
820df8bae1dSRodney W. Grimes 	p->ipf_prev = prev;
821df8bae1dSRodney W. Grimes 	p->ipf_next = prev->ipf_next;
822df8bae1dSRodney W. Grimes 	prev->ipf_next->ipf_prev = p;
823df8bae1dSRodney W. Grimes 	prev->ipf_next = p;
824df8bae1dSRodney W. Grimes }
825df8bae1dSRodney W. Grimes 
826df8bae1dSRodney W. Grimes /*
827df8bae1dSRodney W. Grimes  * To ip_enq as remque is to insque.
828df8bae1dSRodney W. Grimes  */
8290312fbe9SPoul-Henning Kamp static void
830df8bae1dSRodney W. Grimes ip_deq(p)
831df8bae1dSRodney W. Grimes 	register struct ipasfrag *p;
832df8bae1dSRodney W. Grimes {
833df8bae1dSRodney W. Grimes 
834df8bae1dSRodney W. Grimes 	p->ipf_prev->ipf_next = p->ipf_next;
835df8bae1dSRodney W. Grimes 	p->ipf_next->ipf_prev = p->ipf_prev;
836df8bae1dSRodney W. Grimes }
837df8bae1dSRodney W. Grimes 
838df8bae1dSRodney W. Grimes /*
839df8bae1dSRodney W. Grimes  * IP timer processing;
840df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
841df8bae1dSRodney W. Grimes  * queue, discard it.
842df8bae1dSRodney W. Grimes  */
843df8bae1dSRodney W. Grimes void
844df8bae1dSRodney W. Grimes ip_slowtimo()
845df8bae1dSRodney W. Grimes {
846df8bae1dSRodney W. Grimes 	register struct ipq *fp;
847df8bae1dSRodney W. Grimes 	int s = splnet();
848194a213eSAndrey A. Chernov 	int i;
849df8bae1dSRodney W. Grimes 
850194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
851194a213eSAndrey A. Chernov 		fp = ipq[i].next;
852194a213eSAndrey A. Chernov 		if (fp == 0)
853194a213eSAndrey A. Chernov 			continue;
854194a213eSAndrey A. Chernov 		while (fp != &ipq[i]) {
855df8bae1dSRodney W. Grimes 			--fp->ipq_ttl;
856df8bae1dSRodney W. Grimes 			fp = fp->next;
857df8bae1dSRodney W. Grimes 			if (fp->prev->ipq_ttl == 0) {
858df8bae1dSRodney W. Grimes 				ipstat.ips_fragtimeout++;
859df8bae1dSRodney W. Grimes 				ip_freef(fp->prev);
860df8bae1dSRodney W. Grimes 			}
861df8bae1dSRodney W. Grimes 		}
862194a213eSAndrey A. Chernov 	}
863df8bae1dSRodney W. Grimes 	splx(s);
864df8bae1dSRodney W. Grimes }
865df8bae1dSRodney W. Grimes 
866df8bae1dSRodney W. Grimes /*
867df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
868df8bae1dSRodney W. Grimes  */
869df8bae1dSRodney W. Grimes void
870df8bae1dSRodney W. Grimes ip_drain()
871df8bae1dSRodney W. Grimes {
872194a213eSAndrey A. Chernov 	int     i;
873ce29ab3aSGarrett Wollman 
874194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
875194a213eSAndrey A. Chernov 		while (ipq[i].next != &ipq[i]) {
876194a213eSAndrey A. Chernov 			ipstat.ips_fragdropped++;
877194a213eSAndrey A. Chernov 			ip_freef(ipq[i].next);
878194a213eSAndrey A. Chernov 		}
879194a213eSAndrey A. Chernov 	}
880ce29ab3aSGarrett Wollman 	in_rtqdrain();
881df8bae1dSRodney W. Grimes }
882df8bae1dSRodney W. Grimes 
883df8bae1dSRodney W. Grimes /*
884df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
885df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
886df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
887df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
888df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
889df8bae1dSRodney W. Grimes  */
8900312fbe9SPoul-Henning Kamp static int
891df8bae1dSRodney W. Grimes ip_dooptions(m)
892df8bae1dSRodney W. Grimes 	struct mbuf *m;
893df8bae1dSRodney W. Grimes {
894df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
895df8bae1dSRodney W. Grimes 	register u_char *cp;
896df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
897df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
898df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
899df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
900df8bae1dSRodney W. Grimes 	n_time ntime;
901df8bae1dSRodney W. Grimes 
902df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
903df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
90458938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
905df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
906df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
907df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
908df8bae1dSRodney W. Grimes 			break;
909df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
910df8bae1dSRodney W. Grimes 			optlen = 1;
911df8bae1dSRodney W. Grimes 		else {
912df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
913df8bae1dSRodney W. Grimes 			if (optlen <= 0 || optlen > cnt) {
914df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
915df8bae1dSRodney W. Grimes 				goto bad;
916df8bae1dSRodney W. Grimes 			}
917df8bae1dSRodney W. Grimes 		}
918df8bae1dSRodney W. Grimes 		switch (opt) {
919df8bae1dSRodney W. Grimes 
920df8bae1dSRodney W. Grimes 		default:
921df8bae1dSRodney W. Grimes 			break;
922df8bae1dSRodney W. Grimes 
923df8bae1dSRodney W. Grimes 		/*
924df8bae1dSRodney W. Grimes 		 * Source routing with record.
925df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
926df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
927df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
928df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
929df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
930df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
931df8bae1dSRodney W. Grimes 		 */
932df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
933df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
934df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
935df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
936df8bae1dSRodney W. Grimes 				goto bad;
937df8bae1dSRodney W. Grimes 			}
938df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
939df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
940df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
941df8bae1dSRodney W. Grimes 			if (ia == 0) {
942df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
943df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
944df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
945df8bae1dSRodney W. Grimes 					goto bad;
946df8bae1dSRodney W. Grimes 				}
947df8bae1dSRodney W. Grimes 				/*
948df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
949df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
950df8bae1dSRodney W. Grimes 				 */
951df8bae1dSRodney W. Grimes 				break;
952df8bae1dSRodney W. Grimes 			}
953df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
954df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr)) {
955df8bae1dSRodney W. Grimes 				/*
956df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
957df8bae1dSRodney W. Grimes 				 */
958df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
959df8bae1dSRodney W. Grimes 				break;
960df8bae1dSRodney W. Grimes 			}
9611025071fSGarrett Wollman 
9621025071fSGarrett Wollman 			if (!ip_dosourceroute) {
9631025071fSGarrett Wollman 				char buf[4*sizeof "123"];
9641025071fSGarrett Wollman 				strcpy(buf, inet_ntoa(ip->ip_dst));
9651025071fSGarrett Wollman 
9661025071fSGarrett Wollman 				log(LOG_WARNING,
9671025071fSGarrett Wollman 				    "attempted source route from %s to %s\n",
9681025071fSGarrett Wollman 				    inet_ntoa(ip->ip_src), buf);
9691025071fSGarrett Wollman 				type = ICMP_UNREACH;
9701025071fSGarrett Wollman 				code = ICMP_UNREACH_SRCFAIL;
9711025071fSGarrett Wollman 				goto bad;
9721025071fSGarrett Wollman 			}
9731025071fSGarrett Wollman 
974df8bae1dSRodney W. Grimes 			/*
975df8bae1dSRodney W. Grimes 			 * locate outgoing interface
976df8bae1dSRodney W. Grimes 			 */
97794a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
978df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
9791025071fSGarrett Wollman 
980df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
981df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
982df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
983df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
984df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
985df8bae1dSRodney W. Grimes 			} else
986df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
987df8bae1dSRodney W. Grimes 			if (ia == 0) {
988df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
989df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
990df8bae1dSRodney W. Grimes 				goto bad;
991df8bae1dSRodney W. Grimes 			}
992df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
99394a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
99494a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
995df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
996df8bae1dSRodney W. Grimes 			/*
997df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
998df8bae1dSRodney W. Grimes 			 */
999df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1000df8bae1dSRodney W. Grimes 			break;
1001df8bae1dSRodney W. Grimes 
1002df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1003df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1004df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1005df8bae1dSRodney W. Grimes 				goto bad;
1006df8bae1dSRodney W. Grimes 			}
1007df8bae1dSRodney W. Grimes 			/*
1008df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1009df8bae1dSRodney W. Grimes 			 */
1010df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
1011df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr))
1012df8bae1dSRodney W. Grimes 				break;
101394a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1014df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1015df8bae1dSRodney W. Grimes 			/*
1016df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1017df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1018df8bae1dSRodney W. Grimes 			 */
1019df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
1020df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
1021df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1022df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1023df8bae1dSRodney W. Grimes 				goto bad;
1024df8bae1dSRodney W. Grimes 			}
102594a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
102694a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1027df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1028df8bae1dSRodney W. Grimes 			break;
1029df8bae1dSRodney W. Grimes 
1030df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1031df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
1032df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
1033df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
1034df8bae1dSRodney W. Grimes 				goto bad;
1035df8bae1dSRodney W. Grimes 			if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
1036df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
1037df8bae1dSRodney W. Grimes 					goto bad;
1038df8bae1dSRodney W. Grimes 				break;
1039df8bae1dSRodney W. Grimes 			}
1040df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
1041df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
1042df8bae1dSRodney W. Grimes 
1043df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1044df8bae1dSRodney W. Grimes 				break;
1045df8bae1dSRodney W. Grimes 
1046df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
1047df8bae1dSRodney W. Grimes 				if (ipt->ipt_ptr + sizeof(n_time) +
1048df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1049df8bae1dSRodney W. Grimes 					goto bad;
1050df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1051df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1052df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
1053df8bae1dSRodney W. Grimes 				if (ia == 0)
1054df8bae1dSRodney W. Grimes 					continue;
105594a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
105694a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
1057df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1058df8bae1dSRodney W. Grimes 				break;
1059df8bae1dSRodney W. Grimes 
1060df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1061df8bae1dSRodney W. Grimes 				if (ipt->ipt_ptr + sizeof(n_time) +
1062df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1063df8bae1dSRodney W. Grimes 					goto bad;
106494a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1065df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1066df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1067df8bae1dSRodney W. Grimes 					continue;
1068df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1069df8bae1dSRodney W. Grimes 				break;
1070df8bae1dSRodney W. Grimes 
1071df8bae1dSRodney W. Grimes 			default:
1072df8bae1dSRodney W. Grimes 				goto bad;
1073df8bae1dSRodney W. Grimes 			}
1074df8bae1dSRodney W. Grimes 			ntime = iptime();
107594a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1076df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1077df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1078df8bae1dSRodney W. Grimes 		}
1079df8bae1dSRodney W. Grimes 	}
1080df8bae1dSRodney W. Grimes 	if (forward) {
1081df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1082df8bae1dSRodney W. Grimes 		return (1);
1083df8bae1dSRodney W. Grimes 	}
1084df8bae1dSRodney W. Grimes 	return (0);
1085df8bae1dSRodney W. Grimes bad:
108658938916SGarrett Wollman 	ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2;   /* XXX icmp_error adds in hdr length */
1087df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1088df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1089df8bae1dSRodney W. Grimes 	return (1);
1090df8bae1dSRodney W. Grimes }
1091df8bae1dSRodney W. Grimes 
1092df8bae1dSRodney W. Grimes /*
1093df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1094df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1095df8bae1dSRodney W. Grimes  */
10960312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1097df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1098df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1099df8bae1dSRodney W. Grimes {
1100df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1101df8bae1dSRodney W. Grimes 
1102df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1103df8bae1dSRodney W. Grimes 
1104df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1105df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1106df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1107df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1108df8bae1dSRodney W. Grimes 		}
1109df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1110df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1111df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1112df8bae1dSRodney W. Grimes 
11132c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1114df8bae1dSRodney W. Grimes 	}
1115df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1116df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1117df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1118df8bae1dSRodney W. Grimes }
1119df8bae1dSRodney W. Grimes 
1120df8bae1dSRodney W. Grimes /*
1121df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1122df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1123df8bae1dSRodney W. Grimes  */
1124df8bae1dSRodney W. Grimes void
1125df8bae1dSRodney W. Grimes save_rte(option, dst)
1126df8bae1dSRodney W. Grimes 	u_char *option;
1127df8bae1dSRodney W. Grimes 	struct in_addr dst;
1128df8bae1dSRodney W. Grimes {
1129df8bae1dSRodney W. Grimes 	unsigned olen;
1130df8bae1dSRodney W. Grimes 
1131df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1132df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1133df8bae1dSRodney W. Grimes 	if (ipprintfs)
1134df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1135df8bae1dSRodney W. Grimes #endif
1136df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1137df8bae1dSRodney W. Grimes 		return;
11380453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1139df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1140df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1141df8bae1dSRodney W. Grimes }
1142df8bae1dSRodney W. Grimes 
1143df8bae1dSRodney W. Grimes /*
1144df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1145df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1146df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1147df8bae1dSRodney W. Grimes  */
1148df8bae1dSRodney W. Grimes struct mbuf *
1149df8bae1dSRodney W. Grimes ip_srcroute()
1150df8bae1dSRodney W. Grimes {
1151df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1152df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1153df8bae1dSRodney W. Grimes 
1154df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1155df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1156df8bae1dSRodney W. Grimes 	m = m_get(M_DONTWAIT, MT_SOOPTS);
1157df8bae1dSRodney W. Grimes 	if (m == 0)
1158df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1159df8bae1dSRodney W. Grimes 
1160df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1161df8bae1dSRodney W. Grimes 
1162df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1163df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1164df8bae1dSRodney W. Grimes 	    OPTSIZ;
1165df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1166df8bae1dSRodney W. Grimes 	if (ipprintfs)
1167df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1168df8bae1dSRodney W. Grimes #endif
1169df8bae1dSRodney W. Grimes 
1170df8bae1dSRodney W. Grimes 	/*
1171df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1172df8bae1dSRodney W. Grimes 	 */
1173df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1174df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1175df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1176df8bae1dSRodney W. Grimes 	if (ipprintfs)
1177df8bae1dSRodney W. Grimes 		printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
1178df8bae1dSRodney W. Grimes #endif
1179df8bae1dSRodney W. Grimes 
1180df8bae1dSRodney W. Grimes 	/*
1181df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1182df8bae1dSRodney W. Grimes 	 */
1183df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1184df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
118594a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
118694a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1187df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1188df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1189df8bae1dSRodney W. Grimes #undef OPTSIZ
1190df8bae1dSRodney W. Grimes 	/*
1191df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1192df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1193df8bae1dSRodney W. Grimes 	 */
1194df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1195df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1196df8bae1dSRodney W. Grimes 		if (ipprintfs)
1197df8bae1dSRodney W. Grimes 			printf(" %lx", ntohl(q->s_addr));
1198df8bae1dSRodney W. Grimes #endif
1199df8bae1dSRodney W. Grimes 		*q++ = *p--;
1200df8bae1dSRodney W. Grimes 	}
1201df8bae1dSRodney W. Grimes 	/*
1202df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1203df8bae1dSRodney W. Grimes 	 */
1204df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1205df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1206df8bae1dSRodney W. Grimes 	if (ipprintfs)
1207df8bae1dSRodney W. Grimes 		printf(" %lx\n", ntohl(q->s_addr));
1208df8bae1dSRodney W. Grimes #endif
1209df8bae1dSRodney W. Grimes 	return (m);
1210df8bae1dSRodney W. Grimes }
1211df8bae1dSRodney W. Grimes 
1212df8bae1dSRodney W. Grimes /*
1213df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1214df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1215df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1216df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1217df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1218df8bae1dSRodney W. Grimes  */
1219df8bae1dSRodney W. Grimes void
1220df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1221df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1222df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1223df8bae1dSRodney W. Grimes {
1224df8bae1dSRodney W. Grimes 	register int i;
1225df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1226df8bae1dSRodney W. Grimes 	register caddr_t opts;
1227df8bae1dSRodney W. Grimes 	int olen;
1228df8bae1dSRodney W. Grimes 
122958938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1230df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1231df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1232df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1233df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1234df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1235df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
123658938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1237df8bae1dSRodney W. Grimes }
1238df8bae1dSRodney W. Grimes 
1239df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1240df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1241df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1242df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1243df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1244df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1245df8bae1dSRodney W. Grimes 	ENOPROTOOPT
1246df8bae1dSRodney W. Grimes };
1247df8bae1dSRodney W. Grimes 
1248df8bae1dSRodney W. Grimes /*
1249df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1250df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1251df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1252df8bae1dSRodney W. Grimes  * of codes and types.
1253df8bae1dSRodney W. Grimes  *
1254df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1255df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1256df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1257df8bae1dSRodney W. Grimes  * protocol deal with that.
1258df8bae1dSRodney W. Grimes  *
1259df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1260df8bae1dSRodney W. Grimes  * via a source route.
1261df8bae1dSRodney W. Grimes  */
12620312fbe9SPoul-Henning Kamp static void
1263df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1264df8bae1dSRodney W. Grimes 	struct mbuf *m;
1265df8bae1dSRodney W. Grimes 	int srcrt;
1266df8bae1dSRodney W. Grimes {
1267df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1268df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1269df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
127026f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1271df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1272df8bae1dSRodney W. Grimes 	n_long dest;
1273df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
1274df8bae1dSRodney W. Grimes 
1275df8bae1dSRodney W. Grimes 	dest = 0;
1276df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1277df8bae1dSRodney W. Grimes 	if (ipprintfs)
127861ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1279623ae52eSPoul-Henning Kamp 			ip->ip_src.s_addr, ip->ip_dst.s_addr, ip->ip_ttl);
1280df8bae1dSRodney W. Grimes #endif
1281100ba1a6SJordan K. Hubbard 
1282100ba1a6SJordan K. Hubbard 
1283df8bae1dSRodney W. Grimes 	if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
1284df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1285df8bae1dSRodney W. Grimes 		m_freem(m);
1286df8bae1dSRodney W. Grimes 		return;
1287df8bae1dSRodney W. Grimes 	}
1288df8bae1dSRodney W. Grimes 	HTONS(ip->ip_id);
1289df8bae1dSRodney W. Grimes 	if (ip->ip_ttl <= IPTTLDEC) {
1290df8bae1dSRodney W. Grimes 		icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
1291df8bae1dSRodney W. Grimes 		return;
1292df8bae1dSRodney W. Grimes 	}
1293df8bae1dSRodney W. Grimes 	ip->ip_ttl -= IPTTLDEC;
1294df8bae1dSRodney W. Grimes 
1295df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1296df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1297df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1298df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1299df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1300df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1301df8bae1dSRodney W. Grimes 		}
1302df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1303df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1304df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1305df8bae1dSRodney W. Grimes 
13062c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1307df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1308df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1309df8bae1dSRodney W. Grimes 			return;
1310df8bae1dSRodney W. Grimes 		}
1311df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1312df8bae1dSRodney W. Grimes 	}
1313df8bae1dSRodney W. Grimes 
1314df8bae1dSRodney W. Grimes 	/*
1315df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1316df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1317df8bae1dSRodney W. Grimes 	 */
1318df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
1319df8bae1dSRodney W. Grimes 
1320df8bae1dSRodney W. Grimes 	/*
1321df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1322df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1323df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1324df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1325df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1326df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1327df8bae1dSRodney W. Grimes 	 */
1328df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1329df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1330df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1331df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1332df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1333df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1334df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1335df8bae1dSRodney W. Grimes 
1336df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1337df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1338df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1339df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1340df8bae1dSRodney W. Grimes 		    else
1341df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1342df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1343df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1344df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1345df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1346df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1347df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1348df8bae1dSRodney W. Grimes #endif
1349df8bae1dSRodney W. Grimes 		}
1350df8bae1dSRodney W. Grimes 	}
1351df8bae1dSRodney W. Grimes 
1352b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1353b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1354df8bae1dSRodney W. Grimes 	if (error)
1355df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1356df8bae1dSRodney W. Grimes 	else {
1357df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1358df8bae1dSRodney W. Grimes 		if (type)
1359df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1360df8bae1dSRodney W. Grimes 		else {
1361df8bae1dSRodney W. Grimes 			if (mcopy)
1362df8bae1dSRodney W. Grimes 				m_freem(mcopy);
1363df8bae1dSRodney W. Grimes 			return;
1364df8bae1dSRodney W. Grimes 		}
1365df8bae1dSRodney W. Grimes 	}
1366df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1367df8bae1dSRodney W. Grimes 		return;
1368df8bae1dSRodney W. Grimes 	destifp = NULL;
1369df8bae1dSRodney W. Grimes 
1370df8bae1dSRodney W. Grimes 	switch (error) {
1371df8bae1dSRodney W. Grimes 
1372df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1373df8bae1dSRodney W. Grimes 		/* type, code set above */
1374df8bae1dSRodney W. Grimes 		break;
1375df8bae1dSRodney W. Grimes 
1376df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1377df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1378df8bae1dSRodney W. Grimes 	case ENETDOWN:
1379df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1380df8bae1dSRodney W. Grimes 	default:
1381df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1382df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1383df8bae1dSRodney W. Grimes 		break;
1384df8bae1dSRodney W. Grimes 
1385df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1386df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1387df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
1388df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1389df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
1390df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1391df8bae1dSRodney W. Grimes 		break;
1392df8bae1dSRodney W. Grimes 
1393df8bae1dSRodney W. Grimes 	case ENOBUFS:
1394df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1395df8bae1dSRodney W. Grimes 		code = 0;
1396df8bae1dSRodney W. Grimes 		break;
1397df8bae1dSRodney W. Grimes 	}
1398df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1399df8bae1dSRodney W. Grimes }
1400df8bae1dSRodney W. Grimes 
140182c23ebaSBill Fenner void
140282c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
140382c23ebaSBill Fenner 	register struct inpcb *inp;
140482c23ebaSBill Fenner 	register struct mbuf **mp;
140582c23ebaSBill Fenner 	register struct ip *ip;
140682c23ebaSBill Fenner 	register struct mbuf *m;
140782c23ebaSBill Fenner {
140882c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
140982c23ebaSBill Fenner 		struct timeval tv;
141082c23ebaSBill Fenner 
141182c23ebaSBill Fenner 		microtime(&tv);
141282c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
141382c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
141482c23ebaSBill Fenner 		if (*mp)
141582c23ebaSBill Fenner 			mp = &(*mp)->m_next;
141682c23ebaSBill Fenner 	}
141782c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
141882c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
141982c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
142082c23ebaSBill Fenner 		if (*mp)
142182c23ebaSBill Fenner 			mp = &(*mp)->m_next;
142282c23ebaSBill Fenner 	}
142382c23ebaSBill Fenner #ifdef notyet
142482c23ebaSBill Fenner 	/* XXX
142582c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
142682c23ebaSBill Fenner 	 * than they already were.
142782c23ebaSBill Fenner 	 */
142882c23ebaSBill Fenner 	/* options were tossed already */
142982c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
143082c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
143182c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
143282c23ebaSBill Fenner 		if (*mp)
143382c23ebaSBill Fenner 			mp = &(*mp)->m_next;
143482c23ebaSBill Fenner 	}
143582c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
143682c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
143782c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
143882c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
143982c23ebaSBill Fenner 		if (*mp)
144082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
144182c23ebaSBill Fenner 	}
144282c23ebaSBill Fenner #endif
144382c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
144482c23ebaSBill Fenner 		struct sockaddr_dl sdl;
144582c23ebaSBill Fenner 
144682c23ebaSBill Fenner 		sdl.sdl_len = offsetof(struct sockaddr_dl, sdl_data[0]);
144782c23ebaSBill Fenner 		sdl.sdl_family = AF_LINK;
144882c23ebaSBill Fenner 		sdl.sdl_index = m->m_pkthdr.rcvif ?
144982c23ebaSBill Fenner 			m->m_pkthdr.rcvif->if_index : 0;
145082c23ebaSBill Fenner 		sdl.sdl_nlen = sdl.sdl_alen = sdl.sdl_slen = 0;
145182c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &sdl, sdl.sdl_len,
145282c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
145382c23ebaSBill Fenner 		if (*mp)
145482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
145582c23ebaSBill Fenner 	}
145682c23ebaSBill Fenner }
145782c23ebaSBill Fenner 
1458df8bae1dSRodney W. Grimes int
1459f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1460f0068c4aSGarrett Wollman {
1461f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1462f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1463f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1464f0068c4aSGarrett Wollman 
1465f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1466f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1467f0068c4aSGarrett Wollman 
1468f0068c4aSGarrett Wollman 	ip_rsvpd = so;
14691c5de19aSGarrett Wollman 	/*
14701c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
14711c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
14721c5de19aSGarrett Wollman 	 */
14731c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
14741c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
14751c5de19aSGarrett Wollman 		rsvp_on++;
14761c5de19aSGarrett Wollman 	}
1477f0068c4aSGarrett Wollman 
1478f0068c4aSGarrett Wollman 	return 0;
1479f0068c4aSGarrett Wollman }
1480f0068c4aSGarrett Wollman 
1481f0068c4aSGarrett Wollman int
1482f0068c4aSGarrett Wollman ip_rsvp_done(void)
1483f0068c4aSGarrett Wollman {
1484f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
14851c5de19aSGarrett Wollman 	/*
14861c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
14871c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
14881c5de19aSGarrett Wollman 	 */
14891c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
14901c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
14911c5de19aSGarrett Wollman 		rsvp_on--;
14921c5de19aSGarrett Wollman 	}
1493f0068c4aSGarrett Wollman 	return 0;
1494f0068c4aSGarrett Wollman }
1495