xref: /freebsd/sys/netinet/ip_input.c (revision beec821495fdbe25d6d152d53d2adde00f1bef09)
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
34beec8214SDarren Reed  * $Id: ip_input.c,v 1.1.1.2 1997/04/03 10:39:25 darrenr 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;
113f708ef1bSPoul-Henning Kamp static struct ipq ipq;
114f23b4c91SGarrett Wollman 
1150312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1160312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1170312fbe9SPoul-Henning Kamp 	&ip_mtu, 0, "");
1180312fbe9SPoul-Henning Kamp #endif
1190312fbe9SPoul-Henning Kamp 
12058938916SGarrett Wollman #if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1
12158938916SGarrett Wollman #undef COMPAT_IPFW
12258938916SGarrett Wollman #define COMPAT_IPFW 1
12358938916SGarrett Wollman #else
12458938916SGarrett Wollman #undef COMPAT_IPFW
12558938916SGarrett Wollman #endif
12658938916SGarrett Wollman 
12758938916SGarrett Wollman #ifdef COMPAT_IPFW
12823bf9953SPoul-Henning Kamp /* Firewall hooks */
12923bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
13023bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
131e7319babSPoul-Henning Kamp 
132fed1c7e9SSøren Schmidt /* IP Network Address Translation (NAT) hooks */
133fed1c7e9SSøren Schmidt ip_nat_t *ip_nat_ptr;
134fed1c7e9SSøren Schmidt ip_nat_ctl_t *ip_nat_ctl_ptr;
13558938916SGarrett Wollman #endif
136fed1c7e9SSøren Schmidt 
137afed1b49SDarren Reed #if defined(IPFILTER_LKM) || defined(IPFILTER)
138afed1b49SDarren Reed int fr_check __P((struct ip *, int, struct ifnet *, int, struct mbuf **));
139afed1b49SDarren Reed int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)) = NULL;
140afed1b49SDarren Reed #endif
141afed1b49SDarren Reed 
142afed1b49SDarren Reed 
143e7319babSPoul-Henning Kamp /*
144df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
145df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
146df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
147df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
148df8bae1dSRodney W. Grimes  * to us.
149df8bae1dSRodney W. Grimes  */
1500312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
151df8bae1dSRodney W. Grimes static	struct ip_srcrt {
152df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
153df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
154df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
155df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
156df8bae1dSRodney W. Grimes } ip_srcrt;
157df8bae1dSRodney W. Grimes 
15893e0e116SJulian Elischer #ifdef IPDIVERT
15993e0e116SJulian Elischer /*
16093e0e116SJulian Elischer  * Shared variable between ip_input() and ip_reass() to communicate
16193e0e116SJulian Elischer  * about which packets, once assembled from fragments, get diverted,
16293e0e116SJulian Elischer  * and to which port.
16393e0e116SJulian Elischer  */
16493e0e116SJulian Elischer static u_short	frag_divert_port;
16593e0e116SJulian Elischer #endif
16693e0e116SJulian Elischer 
167df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr));
1680312fbe9SPoul-Henning Kamp static void	 ip_deq __P((struct ipasfrag *));
1690312fbe9SPoul-Henning Kamp static int	 ip_dooptions __P((struct mbuf *));
1700312fbe9SPoul-Henning Kamp static void	 ip_enq __P((struct ipasfrag *, struct ipasfrag *));
1710312fbe9SPoul-Henning Kamp static void	 ip_forward __P((struct mbuf *, int));
1720312fbe9SPoul-Henning Kamp static void	 ip_freef __P((struct ipq *));
1730312fbe9SPoul-Henning Kamp static struct ip *
1740312fbe9SPoul-Henning Kamp 	 ip_reass __P((struct ipasfrag *, struct ipq *));
1750312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1760312fbe9SPoul-Henning Kamp 	 ip_rtaddr __P((struct in_addr));
1770312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
178df8bae1dSRodney W. Grimes /*
179df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
180df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
181df8bae1dSRodney W. Grimes  */
182df8bae1dSRodney W. Grimes void
183df8bae1dSRodney W. Grimes ip_init()
184df8bae1dSRodney W. Grimes {
185df8bae1dSRodney W. Grimes 	register struct protosw *pr;
186df8bae1dSRodney W. Grimes 	register int i;
187df8bae1dSRodney W. Grimes 
18859562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
189df8bae1dSRodney W. Grimes 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
190df8bae1dSRodney W. Grimes 	if (pr == 0)
191df8bae1dSRodney W. Grimes 		panic("ip_init");
192df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
193df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
194df8bae1dSRodney W. Grimes 	for (pr = inetdomain.dom_protosw;
195df8bae1dSRodney W. Grimes 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
196df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
197df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
198df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
199df8bae1dSRodney W. Grimes 	ipq.next = ipq.prev = &ipq;
200df8bae1dSRodney W. Grimes 	ip_id = time.tv_sec & 0xffff;
201df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
202b83e4314SPoul-Henning Kamp #ifdef IPFIREWALL
203b83e4314SPoul-Henning Kamp 	ip_fw_init();
204b83e4314SPoul-Henning Kamp #endif
205fed1c7e9SSøren Schmidt #ifdef IPNAT
206fed1c7e9SSøren Schmidt         ip_nat_init();
207fed1c7e9SSøren Schmidt #endif
208fed1c7e9SSøren Schmidt 
209df8bae1dSRodney W. Grimes }
210df8bae1dSRodney W. Grimes 
2110312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
212f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
213df8bae1dSRodney W. Grimes 
214df8bae1dSRodney W. Grimes /*
215df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
216df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
217df8bae1dSRodney W. Grimes  */
218c67b1d17SGarrett Wollman void
219c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
220df8bae1dSRodney W. Grimes {
22123bf9953SPoul-Henning Kamp 	struct ip *ip;
22223bf9953SPoul-Henning Kamp 	struct ipq *fp;
22323bf9953SPoul-Henning Kamp 	struct in_ifaddr *ia;
22409bb5f75SPoul-Henning Kamp 	int hlen;
22547c861ecSBrian Somers 	u_short sum;
226df8bae1dSRodney W. Grimes 
227df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
228df8bae1dSRodney W. Grimes 	if ((m->m_flags & M_PKTHDR) == 0)
22958938916SGarrett Wollman 		panic("ip_input no HDR");
230df8bae1dSRodney W. Grimes #endif
231df8bae1dSRodney W. Grimes 	/*
232df8bae1dSRodney W. Grimes 	 * If no IP addresses have been set yet but the interfaces
233df8bae1dSRodney W. Grimes 	 * are receiving, can't do anything with incoming packets yet.
23459562606SGarrett Wollman 	 * XXX This is broken! We should be able to receive broadcasts
23559562606SGarrett Wollman 	 * and multicasts even without any local addresses configured.
236df8bae1dSRodney W. Grimes 	 */
23759562606SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead))
238df8bae1dSRodney W. Grimes 		goto bad;
239df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
24058938916SGarrett Wollman 
24158938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
24258938916SGarrett Wollman 		goto tooshort;
24358938916SGarrett Wollman 
24458938916SGarrett Wollman #ifdef	DIAGNOSTIC
24558938916SGarrett Wollman 	if (m->m_len < sizeof(struct ip))
24658938916SGarrett Wollman 		panic("ipintr mbuf too short");
24758938916SGarrett Wollman #endif
24858938916SGarrett Wollman 
249df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
250df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
251df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
252c67b1d17SGarrett Wollman 		return;
253df8bae1dSRodney W. Grimes 	}
254df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
25558938916SGarrett Wollman 
25658938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
257df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
258df8bae1dSRodney W. Grimes 		goto bad;
259df8bae1dSRodney W. Grimes 	}
26058938916SGarrett Wollman 
26158938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
262df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
263df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
264df8bae1dSRodney W. Grimes 		goto bad;
265df8bae1dSRodney W. Grimes 	}
266df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
267df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
268df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
269c67b1d17SGarrett Wollman 			return;
270df8bae1dSRodney W. Grimes 		}
271df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
272df8bae1dSRodney W. Grimes 	}
27358938916SGarrett Wollman 	if (hlen == sizeof(struct ip)) {
27447c861ecSBrian Somers 		sum = in_cksum_hdr(ip);
27558938916SGarrett Wollman 	} else {
27647c861ecSBrian Somers 		sum = in_cksum(m, hlen);
27758938916SGarrett Wollman 	}
27847c861ecSBrian Somers 	if (sum) {
279df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
280df8bae1dSRodney W. Grimes 		goto bad;
281df8bae1dSRodney W. Grimes 	}
282df8bae1dSRodney W. Grimes 
283df8bae1dSRodney W. Grimes 	/*
284df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
285df8bae1dSRodney W. Grimes 	 */
286df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
287df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
288df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
289df8bae1dSRodney W. Grimes 		goto bad;
290df8bae1dSRodney W. Grimes 	}
291df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_id);
292df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
293df8bae1dSRodney W. Grimes 
294df8bae1dSRodney W. Grimes 	/*
295df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
296df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
297df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
298df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
299df8bae1dSRodney W. Grimes 	 */
300df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
30158938916SGarrett Wollman tooshort:
302df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
303df8bae1dSRodney W. Grimes 		goto bad;
304df8bae1dSRodney W. Grimes 	}
305df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
306df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
307df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
308df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
309df8bae1dSRodney W. Grimes 		} else
310df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
311df8bae1dSRodney W. Grimes 	}
3124dd1662bSUgen J.S. Antsilevich 	/*
3134dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3144dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
3154dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
3164dd1662bSUgen J.S. Antsilevich 	 * deals with it.
31793e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
318fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
3194dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
3204dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
3214dd1662bSUgen J.S. Antsilevich  	 */
322beec8214SDarren Reed #if defined(IPFILTER) || defined(IPFILTER_LKM)
323beec8214SDarren Reed 	/*
324beec8214SDarren Reed 	 * Check if we want to allow this packet to be processed.
325beec8214SDarren Reed 	 * Consider it to be bad if not.
326beec8214SDarren Reed 	 */
327beec8214SDarren Reed 	if (fr_check) {
328beec8214SDarren Reed 		struct	mbuf	*m1 = m;
329df8bae1dSRodney W. Grimes 
330beec8214SDarren Reed 		if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m1) || !m1)
331beec8214SDarren Reed 			return;
332beec8214SDarren Reed 		ip = mtod(m = m1, struct ip *);
333beec8214SDarren Reed 	}
334beec8214SDarren Reed #endif
33558938916SGarrett Wollman #ifdef COMPAT_IPFW
33693e0e116SJulian Elischer 	if (ip_fw_chk_ptr) {
33793e0e116SJulian Elischer 		int action;
33893e0e116SJulian Elischer 
33993e0e116SJulian Elischer #ifdef IPDIVERT
34093e0e116SJulian Elischer 		action = (*ip_fw_chk_ptr)(&ip, hlen,
34193e0e116SJulian Elischer 				m->m_pkthdr.rcvif, ip_divert_ignore, &m);
342d81e4043SBrian Somers 		ip_divert_ignore = 0;
34393e0e116SJulian Elischer #else
34493e0e116SJulian Elischer 		action = (*ip_fw_chk_ptr)(&ip, hlen, m->m_pkthdr.rcvif, 0, &m);
34593e0e116SJulian Elischer #endif
34693e0e116SJulian Elischer 		if (action == -1)
347539e53baSPoul-Henning Kamp 			return;
34893e0e116SJulian Elischer 		if (action != 0) {
34993e0e116SJulian Elischer #ifdef IPDIVERT
35093e0e116SJulian Elischer 			frag_divert_port = action;
35193e0e116SJulian Elischer 			goto ours;
35293e0e116SJulian Elischer #else
35393e0e116SJulian Elischer 			goto bad;	/* ipfw said divert but we can't */
35493e0e116SJulian Elischer #endif
35593e0e116SJulian Elischer 		}
35693e0e116SJulian Elischer 	}
357100ba1a6SJordan K. Hubbard 
3586713d4a7SSøren Schmidt         if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN))
359fed1c7e9SSøren Schmidt 		return;
36058938916SGarrett Wollman #endif
361fed1c7e9SSøren Schmidt 
362df8bae1dSRodney W. Grimes 	/*
363df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
364df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
365df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
366df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
367df8bae1dSRodney W. Grimes 	 */
368df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
369df8bae1dSRodney W. Grimes 	if (hlen > sizeof (struct ip) && ip_dooptions(m))
370c67b1d17SGarrett Wollman 		return;
371df8bae1dSRodney W. Grimes 
372f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
373f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
374f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
375f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
376f0068c4aSGarrett Wollman 	 * grabbing the packet.
377f0068c4aSGarrett Wollman          */
3781c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
379f0068c4aSGarrett Wollman 		goto ours;
380f0068c4aSGarrett Wollman 
381df8bae1dSRodney W. Grimes 	/*
382df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
383df8bae1dSRodney W. Grimes 	 */
38459562606SGarrett Wollman 	for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
385df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
386df8bae1dSRodney W. Grimes 
387df8bae1dSRodney W. Grimes 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
388df8bae1dSRodney W. Grimes 			goto ours;
3896ed666afSPoul-Henning Kamp 		if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
390df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
391df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr)
392df8bae1dSRodney W. Grimes 				goto ours;
393df8bae1dSRodney W. Grimes 			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
394df8bae1dSRodney W. Grimes 				goto ours;
395df8bae1dSRodney W. Grimes 		}
396df8bae1dSRodney W. Grimes 	}
397df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
398df8bae1dSRodney W. Grimes 		struct in_multi *inm;
399df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
400df8bae1dSRodney W. Grimes 			/*
401df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
402df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
403df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
404df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
405df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
406df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
407df8bae1dSRodney W. Grimes 			 *
408df8bae1dSRodney W. Grimes 			 * (The IP ident field is put in the same byte order
409df8bae1dSRodney W. Grimes 			 * as expected when ip_mforward() is called from
410df8bae1dSRodney W. Grimes 			 * ip_output().)
411df8bae1dSRodney W. Grimes 			 */
412df8bae1dSRodney W. Grimes 			ip->ip_id = htons(ip->ip_id);
413f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
414df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
415df8bae1dSRodney W. Grimes 				m_freem(m);
416c67b1d17SGarrett Wollman 				return;
417df8bae1dSRodney W. Grimes 			}
418df8bae1dSRodney W. Grimes 			ip->ip_id = ntohs(ip->ip_id);
419df8bae1dSRodney W. Grimes 
420df8bae1dSRodney W. Grimes 			/*
421df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
422df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
423df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
424df8bae1dSRodney W. Grimes 			 */
425df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
426df8bae1dSRodney W. Grimes 				goto ours;
427df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
428df8bae1dSRodney W. Grimes 		}
429df8bae1dSRodney W. Grimes 		/*
430df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
431df8bae1dSRodney W. Grimes 		 * arrival interface.
432df8bae1dSRodney W. Grimes 		 */
433df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
434df8bae1dSRodney W. Grimes 		if (inm == NULL) {
43582c39223SGarrett Wollman 			ipstat.ips_notmember++;
436df8bae1dSRodney W. Grimes 			m_freem(m);
437c67b1d17SGarrett Wollman 			return;
438df8bae1dSRodney W. Grimes 		}
439df8bae1dSRodney W. Grimes 		goto ours;
440df8bae1dSRodney W. Grimes 	}
441df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
442df8bae1dSRodney W. Grimes 		goto ours;
443df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
444df8bae1dSRodney W. Grimes 		goto ours;
445df8bae1dSRodney W. Grimes 
446df8bae1dSRodney W. Grimes 	/*
447df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
448df8bae1dSRodney W. Grimes 	 */
449df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
450df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
451df8bae1dSRodney W. Grimes 		m_freem(m);
452df8bae1dSRodney W. Grimes 	} else
453df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
454c67b1d17SGarrett Wollman 	return;
455df8bae1dSRodney W. Grimes 
456df8bae1dSRodney W. Grimes ours:
457100ba1a6SJordan K. Hubbard 
45863f8d699SJordan K. Hubbard 	/*
459df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
460df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
461df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
462df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
463df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
464df8bae1dSRodney W. Grimes 	 */
465c7a6ccb3SDavid Greenman 	if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
466df8bae1dSRodney W. Grimes 		if (m->m_flags & M_EXT) {		/* XXX */
467df8bae1dSRodney W. Grimes 			if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
468df8bae1dSRodney W. Grimes 				ipstat.ips_toosmall++;
46993e0e116SJulian Elischer #ifdef IPDIVERT
47093e0e116SJulian Elischer 				frag_divert_port = 0;
47193e0e116SJulian Elischer #endif
472c67b1d17SGarrett Wollman 				return;
473df8bae1dSRodney W. Grimes 			}
474df8bae1dSRodney W. Grimes 			ip = mtod(m, struct ip *);
475df8bae1dSRodney W. Grimes 		}
476df8bae1dSRodney W. Grimes 		/*
477df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
478df8bae1dSRodney W. Grimes 		 * of this datagram.
479df8bae1dSRodney W. Grimes 		 */
480df8bae1dSRodney W. Grimes 		for (fp = ipq.next; fp != &ipq; fp = fp->next)
481df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
482df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
483df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
484df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
485df8bae1dSRodney W. Grimes 				goto found;
486df8bae1dSRodney W. Grimes 		fp = 0;
487df8bae1dSRodney W. Grimes found:
488df8bae1dSRodney W. Grimes 
489df8bae1dSRodney W. Grimes 		/*
490df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
491df8bae1dSRodney W. Grimes 		 * set ip_mff if more fragments are expected,
492df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
493df8bae1dSRodney W. Grimes 		 */
494df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
495df8bae1dSRodney W. Grimes 		((struct ipasfrag *)ip)->ipf_mff &= ~1;
496df8bae1dSRodney W. Grimes 		if (ip->ip_off & IP_MF)
497df8bae1dSRodney W. Grimes 			((struct ipasfrag *)ip)->ipf_mff |= 1;
498df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
499df8bae1dSRodney W. Grimes 
500df8bae1dSRodney W. Grimes 		/*
501df8bae1dSRodney W. Grimes 		 * If datagram marked as having more fragments
502df8bae1dSRodney W. Grimes 		 * or if this is not the first fragment,
503df8bae1dSRodney W. Grimes 		 * attempt reassembly; if it succeeds, proceed.
504df8bae1dSRodney W. Grimes 		 */
505df8bae1dSRodney W. Grimes 		if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
506df8bae1dSRodney W. Grimes 			ipstat.ips_fragments++;
507df8bae1dSRodney W. Grimes 			ip = ip_reass((struct ipasfrag *)ip, fp);
508df8bae1dSRodney W. Grimes 			if (ip == 0)
509c67b1d17SGarrett Wollman 				return;
510df8bae1dSRodney W. Grimes 			ipstat.ips_reassembled++;
511df8bae1dSRodney W. Grimes 			m = dtom(ip);
512df8bae1dSRodney W. Grimes 		} else
513df8bae1dSRodney W. Grimes 			if (fp)
514df8bae1dSRodney W. Grimes 				ip_freef(fp);
515df8bae1dSRodney W. Grimes 	} else
516df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
517df8bae1dSRodney W. Grimes 
51893e0e116SJulian Elischer #ifdef IPDIVERT
51993e0e116SJulian Elischer 	/*
52093e0e116SJulian Elischer 	 * Divert packets here to the divert protocol if required
52193e0e116SJulian Elischer 	 */
52293e0e116SJulian Elischer 	if (frag_divert_port) {
52393e0e116SJulian Elischer 		ip_divert_port = frag_divert_port;
52493e0e116SJulian Elischer 		frag_divert_port = 0;
52593e0e116SJulian Elischer 		(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);
52693e0e116SJulian Elischer 		return;
52793e0e116SJulian Elischer 	}
52893e0e116SJulian Elischer #endif
52993e0e116SJulian Elischer 
530df8bae1dSRodney W. Grimes 	/*
531df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
532df8bae1dSRodney W. Grimes 	 */
533df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
534df8bae1dSRodney W. Grimes 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
535c67b1d17SGarrett Wollman 	return;
536df8bae1dSRodney W. Grimes bad:
537df8bae1dSRodney W. Grimes 	m_freem(m);
538c67b1d17SGarrett Wollman }
539c67b1d17SGarrett Wollman 
540c67b1d17SGarrett Wollman /*
541c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
542c67b1d17SGarrett Wollman  */
543c67b1d17SGarrett Wollman static void
544c67b1d17SGarrett Wollman ipintr(void)
545c67b1d17SGarrett Wollman {
546c67b1d17SGarrett Wollman 	int s;
547c67b1d17SGarrett Wollman 	struct mbuf *m;
548c67b1d17SGarrett Wollman 
549c67b1d17SGarrett Wollman 	while(1) {
550c67b1d17SGarrett Wollman 		s = splimp();
551c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
552c67b1d17SGarrett Wollman 		splx(s);
553c67b1d17SGarrett Wollman 		if (m == 0)
554c67b1d17SGarrett Wollman 			return;
555c67b1d17SGarrett Wollman 		ip_input(m);
556c67b1d17SGarrett Wollman 	}
557df8bae1dSRodney W. Grimes }
558df8bae1dSRodney W. Grimes 
559748e0b0aSGarrett Wollman NETISR_SET(NETISR_IP, ipintr);
560748e0b0aSGarrett Wollman 
561df8bae1dSRodney W. Grimes /*
562df8bae1dSRodney W. Grimes  * Take incoming datagram fragment and try to
563df8bae1dSRodney W. Grimes  * reassemble it into whole datagram.  If a chain for
564df8bae1dSRodney W. Grimes  * reassembly of this datagram already exists, then it
565df8bae1dSRodney W. Grimes  * is given as fp; otherwise have to make a chain.
566df8bae1dSRodney W. Grimes  */
5670312fbe9SPoul-Henning Kamp static struct ip *
568df8bae1dSRodney W. Grimes ip_reass(ip, fp)
569df8bae1dSRodney W. Grimes 	register struct ipasfrag *ip;
570df8bae1dSRodney W. Grimes 	register struct ipq *fp;
571df8bae1dSRodney W. Grimes {
572df8bae1dSRodney W. Grimes 	register struct mbuf *m = dtom(ip);
573df8bae1dSRodney W. Grimes 	register struct ipasfrag *q;
574df8bae1dSRodney W. Grimes 	struct mbuf *t;
575df8bae1dSRodney W. Grimes 	int hlen = ip->ip_hl << 2;
576df8bae1dSRodney W. Grimes 	int i, next;
577df8bae1dSRodney W. Grimes 
578df8bae1dSRodney W. Grimes 	/*
579df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
580df8bae1dSRodney W. Grimes 	 * would confuse code below.
581df8bae1dSRodney W. Grimes 	 */
582df8bae1dSRodney W. Grimes 	m->m_data += hlen;
583df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
584df8bae1dSRodney W. Grimes 
585df8bae1dSRodney W. Grimes 	/*
586df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
587df8bae1dSRodney W. Grimes 	 */
588df8bae1dSRodney W. Grimes 	if (fp == 0) {
589df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
590df8bae1dSRodney W. Grimes 			goto dropfrag;
591df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
592df8bae1dSRodney W. Grimes 		insque(fp, &ipq);
593df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
594df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
595df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
596df8bae1dSRodney W. Grimes 		fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
597df8bae1dSRodney W. Grimes 		fp->ipq_src = ((struct ip *)ip)->ip_src;
598df8bae1dSRodney W. Grimes 		fp->ipq_dst = ((struct ip *)ip)->ip_dst;
59993e0e116SJulian Elischer #ifdef IPDIVERT
60093e0e116SJulian Elischer 		fp->ipq_divert = 0;
60193e0e116SJulian Elischer #endif
602df8bae1dSRodney W. Grimes 		q = (struct ipasfrag *)fp;
603df8bae1dSRodney W. Grimes 		goto insert;
604df8bae1dSRodney W. Grimes 	}
605df8bae1dSRodney W. Grimes 
606df8bae1dSRodney W. Grimes 	/*
607df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
608df8bae1dSRodney W. Grimes 	 */
609df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
610df8bae1dSRodney W. Grimes 		if (q->ip_off > ip->ip_off)
611df8bae1dSRodney W. Grimes 			break;
612df8bae1dSRodney W. Grimes 
613df8bae1dSRodney W. Grimes 	/*
614df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
615df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
616df8bae1dSRodney W. Grimes 	 * segment.  If it provides all of our data, drop us.
617df8bae1dSRodney W. Grimes 	 */
618df8bae1dSRodney W. Grimes 	if (q->ipf_prev != (struct ipasfrag *)fp) {
619df8bae1dSRodney W. Grimes 		i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
620df8bae1dSRodney W. Grimes 		if (i > 0) {
621df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
622df8bae1dSRodney W. Grimes 				goto dropfrag;
623df8bae1dSRodney W. Grimes 			m_adj(dtom(ip), i);
624df8bae1dSRodney W. Grimes 			ip->ip_off += i;
625df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
626df8bae1dSRodney W. Grimes 		}
627df8bae1dSRodney W. Grimes 	}
628df8bae1dSRodney W. Grimes 
629df8bae1dSRodney W. Grimes 	/*
630df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
631df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
632df8bae1dSRodney W. Grimes 	 */
633df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
634e7c81944SDavid Greenman 		struct mbuf *m0;
635e7c81944SDavid Greenman 
636df8bae1dSRodney W. Grimes 		i = (ip->ip_off + ip->ip_len) - q->ip_off;
637df8bae1dSRodney W. Grimes 		if (i < q->ip_len) {
638df8bae1dSRodney W. Grimes 			q->ip_len -= i;
639df8bae1dSRodney W. Grimes 			q->ip_off += i;
640df8bae1dSRodney W. Grimes 			m_adj(dtom(q), i);
641df8bae1dSRodney W. Grimes 			break;
642df8bae1dSRodney W. Grimes 		}
643e7c81944SDavid Greenman 		m0 = dtom(q);
644df8bae1dSRodney W. Grimes 		q = q->ipf_next;
645df8bae1dSRodney W. Grimes 		ip_deq(q->ipf_prev);
646e7c81944SDavid Greenman 		m_freem(m0);
647df8bae1dSRodney W. Grimes 	}
648df8bae1dSRodney W. Grimes 
649df8bae1dSRodney W. Grimes insert:
65093e0e116SJulian Elischer 
65193e0e116SJulian Elischer #ifdef IPDIVERT
65293e0e116SJulian Elischer 	/*
65393e0e116SJulian Elischer 	 * Any fragment diverting causes the whole packet to divert
65493e0e116SJulian Elischer 	 */
65593e0e116SJulian Elischer 	if (frag_divert_port != 0)
65693e0e116SJulian Elischer 		fp->ipq_divert = frag_divert_port;
65793e0e116SJulian Elischer 	frag_divert_port = 0;
65893e0e116SJulian Elischer #endif
65993e0e116SJulian Elischer 
660df8bae1dSRodney W. Grimes 	/*
661df8bae1dSRodney W. Grimes 	 * Stick new segment in its place;
662df8bae1dSRodney W. Grimes 	 * check for complete reassembly.
663df8bae1dSRodney W. Grimes 	 */
664df8bae1dSRodney W. Grimes 	ip_enq(ip, q->ipf_prev);
665df8bae1dSRodney W. Grimes 	next = 0;
666df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
667df8bae1dSRodney W. Grimes 		if (q->ip_off != next)
668df8bae1dSRodney W. Grimes 			return (0);
669df8bae1dSRodney W. Grimes 		next += q->ip_len;
670df8bae1dSRodney W. Grimes 	}
671df8bae1dSRodney W. Grimes 	if (q->ipf_prev->ipf_mff & 1)
672df8bae1dSRodney W. Grimes 		return (0);
673df8bae1dSRodney W. Grimes 
674df8bae1dSRodney W. Grimes 	/*
675430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
676430d30d8SBill Fenner 	 */
677430d30d8SBill Fenner 	if (next + (IP_VHL_HL(((struct ip *)fp->ipq_next)->ip_vhl) << 2)
678430d30d8SBill Fenner 							> IP_MAXPACKET) {
679430d30d8SBill Fenner 		ipstat.ips_toolong++;
680430d30d8SBill Fenner 		ip_freef(fp);
681430d30d8SBill Fenner 		return (0);
682430d30d8SBill Fenner 	}
683430d30d8SBill Fenner 
684430d30d8SBill Fenner 	/*
685430d30d8SBill Fenner 	 * Concatenate fragments.
686df8bae1dSRodney W. Grimes 	 */
687df8bae1dSRodney W. Grimes 	q = fp->ipq_next;
688df8bae1dSRodney W. Grimes 	m = dtom(q);
689df8bae1dSRodney W. Grimes 	t = m->m_next;
690df8bae1dSRodney W. Grimes 	m->m_next = 0;
691df8bae1dSRodney W. Grimes 	m_cat(m, t);
692df8bae1dSRodney W. Grimes 	q = q->ipf_next;
693df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp) {
694df8bae1dSRodney W. Grimes 		t = dtom(q);
695df8bae1dSRodney W. Grimes 		q = q->ipf_next;
696df8bae1dSRodney W. Grimes 		m_cat(m, t);
697df8bae1dSRodney W. Grimes 	}
698df8bae1dSRodney W. Grimes 
69993e0e116SJulian Elischer #ifdef IPDIVERT
70093e0e116SJulian Elischer 	/*
70193e0e116SJulian Elischer 	 * Record divert port for packet, if any
70293e0e116SJulian Elischer 	 */
70393e0e116SJulian Elischer 	frag_divert_port = fp->ipq_divert;
70493e0e116SJulian Elischer #endif
70593e0e116SJulian Elischer 
706df8bae1dSRodney W. Grimes 	/*
707df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
708df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
709df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
710df8bae1dSRodney W. Grimes 	 * Make header visible.
711df8bae1dSRodney W. Grimes 	 */
712df8bae1dSRodney W. Grimes 	ip = fp->ipq_next;
713df8bae1dSRodney W. Grimes 	ip->ip_len = next;
714df8bae1dSRodney W. Grimes 	ip->ipf_mff &= ~1;
715df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_src = fp->ipq_src;
716df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_dst = fp->ipq_dst;
717df8bae1dSRodney W. Grimes 	remque(fp);
718df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
719df8bae1dSRodney W. Grimes 	m = dtom(ip);
720df8bae1dSRodney W. Grimes 	m->m_len += (ip->ip_hl << 2);
721df8bae1dSRodney W. Grimes 	m->m_data -= (ip->ip_hl << 2);
722df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
723df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
724df8bae1dSRodney W. Grimes 		register int plen = 0;
725df8bae1dSRodney W. Grimes 		for (t = m; m; m = m->m_next)
726df8bae1dSRodney W. Grimes 			plen += m->m_len;
727df8bae1dSRodney W. Grimes 		t->m_pkthdr.len = plen;
728df8bae1dSRodney W. Grimes 	}
729df8bae1dSRodney W. Grimes 	return ((struct ip *)ip);
730df8bae1dSRodney W. Grimes 
731df8bae1dSRodney W. Grimes dropfrag:
732df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
733df8bae1dSRodney W. Grimes 	m_freem(m);
734df8bae1dSRodney W. Grimes 	return (0);
735df8bae1dSRodney W. Grimes }
736df8bae1dSRodney W. Grimes 
737df8bae1dSRodney W. Grimes /*
738df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
739df8bae1dSRodney W. Grimes  * associated datagrams.
740df8bae1dSRodney W. Grimes  */
7410312fbe9SPoul-Henning Kamp static void
742df8bae1dSRodney W. Grimes ip_freef(fp)
743df8bae1dSRodney W. Grimes 	struct ipq *fp;
744df8bae1dSRodney W. Grimes {
745df8bae1dSRodney W. Grimes 	register struct ipasfrag *q, *p;
746df8bae1dSRodney W. Grimes 
747df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
748df8bae1dSRodney W. Grimes 		p = q->ipf_next;
749df8bae1dSRodney W. Grimes 		ip_deq(q);
750df8bae1dSRodney W. Grimes 		m_freem(dtom(q));
751df8bae1dSRodney W. Grimes 	}
752df8bae1dSRodney W. Grimes 	remque(fp);
753df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
754df8bae1dSRodney W. Grimes }
755df8bae1dSRodney W. Grimes 
756df8bae1dSRodney W. Grimes /*
757df8bae1dSRodney W. Grimes  * Put an ip fragment on a reassembly chain.
758df8bae1dSRodney W. Grimes  * Like insque, but pointers in middle of structure.
759df8bae1dSRodney W. Grimes  */
7600312fbe9SPoul-Henning Kamp static void
761df8bae1dSRodney W. Grimes ip_enq(p, prev)
762df8bae1dSRodney W. Grimes 	register struct ipasfrag *p, *prev;
763df8bae1dSRodney W. Grimes {
764df8bae1dSRodney W. Grimes 
765df8bae1dSRodney W. Grimes 	p->ipf_prev = prev;
766df8bae1dSRodney W. Grimes 	p->ipf_next = prev->ipf_next;
767df8bae1dSRodney W. Grimes 	prev->ipf_next->ipf_prev = p;
768df8bae1dSRodney W. Grimes 	prev->ipf_next = p;
769df8bae1dSRodney W. Grimes }
770df8bae1dSRodney W. Grimes 
771df8bae1dSRodney W. Grimes /*
772df8bae1dSRodney W. Grimes  * To ip_enq as remque is to insque.
773df8bae1dSRodney W. Grimes  */
7740312fbe9SPoul-Henning Kamp static void
775df8bae1dSRodney W. Grimes ip_deq(p)
776df8bae1dSRodney W. Grimes 	register struct ipasfrag *p;
777df8bae1dSRodney W. Grimes {
778df8bae1dSRodney W. Grimes 
779df8bae1dSRodney W. Grimes 	p->ipf_prev->ipf_next = p->ipf_next;
780df8bae1dSRodney W. Grimes 	p->ipf_next->ipf_prev = p->ipf_prev;
781df8bae1dSRodney W. Grimes }
782df8bae1dSRodney W. Grimes 
783df8bae1dSRodney W. Grimes /*
784df8bae1dSRodney W. Grimes  * IP timer processing;
785df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
786df8bae1dSRodney W. Grimes  * queue, discard it.
787df8bae1dSRodney W. Grimes  */
788df8bae1dSRodney W. Grimes void
789df8bae1dSRodney W. Grimes ip_slowtimo()
790df8bae1dSRodney W. Grimes {
791df8bae1dSRodney W. Grimes 	register struct ipq *fp;
792df8bae1dSRodney W. Grimes 	int s = splnet();
793df8bae1dSRodney W. Grimes 
794df8bae1dSRodney W. Grimes 	fp = ipq.next;
795df8bae1dSRodney W. Grimes 	if (fp == 0) {
796df8bae1dSRodney W. Grimes 		splx(s);
797df8bae1dSRodney W. Grimes 		return;
798df8bae1dSRodney W. Grimes 	}
799df8bae1dSRodney W. Grimes 	while (fp != &ipq) {
800df8bae1dSRodney W. Grimes 		--fp->ipq_ttl;
801df8bae1dSRodney W. Grimes 		fp = fp->next;
802df8bae1dSRodney W. Grimes 		if (fp->prev->ipq_ttl == 0) {
803df8bae1dSRodney W. Grimes 			ipstat.ips_fragtimeout++;
804df8bae1dSRodney W. Grimes 			ip_freef(fp->prev);
805df8bae1dSRodney W. Grimes 		}
806df8bae1dSRodney W. Grimes 	}
807df8bae1dSRodney W. Grimes 	splx(s);
808df8bae1dSRodney W. Grimes }
809df8bae1dSRodney W. Grimes 
810df8bae1dSRodney W. Grimes /*
811df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
812df8bae1dSRodney W. Grimes  */
813df8bae1dSRodney W. Grimes void
814df8bae1dSRodney W. Grimes ip_drain()
815df8bae1dSRodney W. Grimes {
816df8bae1dSRodney W. Grimes 	while (ipq.next != &ipq) {
817df8bae1dSRodney W. Grimes 		ipstat.ips_fragdropped++;
818df8bae1dSRodney W. Grimes 		ip_freef(ipq.next);
819df8bae1dSRodney W. Grimes 	}
820ce29ab3aSGarrett Wollman 
821ce29ab3aSGarrett Wollman 	in_rtqdrain();
822df8bae1dSRodney W. Grimes }
823df8bae1dSRodney W. Grimes 
824df8bae1dSRodney W. Grimes /*
825df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
826df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
827df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
828df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
829df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
830df8bae1dSRodney W. Grimes  */
8310312fbe9SPoul-Henning Kamp static int
832df8bae1dSRodney W. Grimes ip_dooptions(m)
833df8bae1dSRodney W. Grimes 	struct mbuf *m;
834df8bae1dSRodney W. Grimes {
835df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
836df8bae1dSRodney W. Grimes 	register u_char *cp;
837df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
838df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
839df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
840df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
841df8bae1dSRodney W. Grimes 	n_time ntime;
842df8bae1dSRodney W. Grimes 
843df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
844df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
84558938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
846df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
847df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
848df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
849df8bae1dSRodney W. Grimes 			break;
850df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
851df8bae1dSRodney W. Grimes 			optlen = 1;
852df8bae1dSRodney W. Grimes 		else {
853df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
854df8bae1dSRodney W. Grimes 			if (optlen <= 0 || optlen > cnt) {
855df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
856df8bae1dSRodney W. Grimes 				goto bad;
857df8bae1dSRodney W. Grimes 			}
858df8bae1dSRodney W. Grimes 		}
859df8bae1dSRodney W. Grimes 		switch (opt) {
860df8bae1dSRodney W. Grimes 
861df8bae1dSRodney W. Grimes 		default:
862df8bae1dSRodney W. Grimes 			break;
863df8bae1dSRodney W. Grimes 
864df8bae1dSRodney W. Grimes 		/*
865df8bae1dSRodney W. Grimes 		 * Source routing with record.
866df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
867df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
868df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
869df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
870df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
871df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
872df8bae1dSRodney W. Grimes 		 */
873df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
874df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
875df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
876df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
877df8bae1dSRodney W. Grimes 				goto bad;
878df8bae1dSRodney W. Grimes 			}
879df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
880df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
881df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
882df8bae1dSRodney W. Grimes 			if (ia == 0) {
883df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
884df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
885df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
886df8bae1dSRodney W. Grimes 					goto bad;
887df8bae1dSRodney W. Grimes 				}
888df8bae1dSRodney W. Grimes 				/*
889df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
890df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
891df8bae1dSRodney W. Grimes 				 */
892df8bae1dSRodney W. Grimes 				break;
893df8bae1dSRodney W. Grimes 			}
894df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
895df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr)) {
896df8bae1dSRodney W. Grimes 				/*
897df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
898df8bae1dSRodney W. Grimes 				 */
899df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
900df8bae1dSRodney W. Grimes 				break;
901df8bae1dSRodney W. Grimes 			}
9021025071fSGarrett Wollman 
9031025071fSGarrett Wollman 			if (!ip_dosourceroute) {
9041025071fSGarrett Wollman 				char buf[4*sizeof "123"];
9051025071fSGarrett Wollman 				strcpy(buf, inet_ntoa(ip->ip_dst));
9061025071fSGarrett Wollman 
9071025071fSGarrett Wollman 				log(LOG_WARNING,
9081025071fSGarrett Wollman 				    "attempted source route from %s to %s\n",
9091025071fSGarrett Wollman 				    inet_ntoa(ip->ip_src), buf);
9101025071fSGarrett Wollman 				type = ICMP_UNREACH;
9111025071fSGarrett Wollman 				code = ICMP_UNREACH_SRCFAIL;
9121025071fSGarrett Wollman 				goto bad;
9131025071fSGarrett Wollman 			}
9141025071fSGarrett Wollman 
915df8bae1dSRodney W. Grimes 			/*
916df8bae1dSRodney W. Grimes 			 * locate outgoing interface
917df8bae1dSRodney W. Grimes 			 */
91894a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
919df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
9201025071fSGarrett Wollman 
921df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
922df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
923df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
924df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
925df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
926df8bae1dSRodney W. Grimes 			} else
927df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
928df8bae1dSRodney W. Grimes 			if (ia == 0) {
929df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
930df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
931df8bae1dSRodney W. Grimes 				goto bad;
932df8bae1dSRodney W. Grimes 			}
933df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
93494a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
93594a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
936df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
937df8bae1dSRodney W. Grimes 			/*
938df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
939df8bae1dSRodney W. Grimes 			 */
940df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
941df8bae1dSRodney W. Grimes 			break;
942df8bae1dSRodney W. Grimes 
943df8bae1dSRodney W. Grimes 		case IPOPT_RR:
944df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
945df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
946df8bae1dSRodney W. Grimes 				goto bad;
947df8bae1dSRodney W. Grimes 			}
948df8bae1dSRodney W. Grimes 			/*
949df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
950df8bae1dSRodney W. Grimes 			 */
951df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
952df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr))
953df8bae1dSRodney W. Grimes 				break;
95494a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
955df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
956df8bae1dSRodney W. Grimes 			/*
957df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
958df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
959df8bae1dSRodney W. Grimes 			 */
960df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
961df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
962df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
963df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
964df8bae1dSRodney W. Grimes 				goto bad;
965df8bae1dSRodney W. Grimes 			}
96694a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
96794a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
968df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
969df8bae1dSRodney W. Grimes 			break;
970df8bae1dSRodney W. Grimes 
971df8bae1dSRodney W. Grimes 		case IPOPT_TS:
972df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
973df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
974df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
975df8bae1dSRodney W. Grimes 				goto bad;
976df8bae1dSRodney W. Grimes 			if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
977df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
978df8bae1dSRodney W. Grimes 					goto bad;
979df8bae1dSRodney W. Grimes 				break;
980df8bae1dSRodney W. Grimes 			}
981df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
982df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
983df8bae1dSRodney W. Grimes 
984df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
985df8bae1dSRodney W. Grimes 				break;
986df8bae1dSRodney W. Grimes 
987df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
988df8bae1dSRodney W. Grimes 				if (ipt->ipt_ptr + sizeof(n_time) +
989df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
990df8bae1dSRodney W. Grimes 					goto bad;
991df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
992df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
993df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
994df8bae1dSRodney W. Grimes 				if (ia == 0)
995df8bae1dSRodney W. Grimes 					continue;
99694a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
99794a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
998df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
999df8bae1dSRodney W. Grimes 				break;
1000df8bae1dSRodney W. Grimes 
1001df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1002df8bae1dSRodney W. Grimes 				if (ipt->ipt_ptr + sizeof(n_time) +
1003df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1004df8bae1dSRodney W. Grimes 					goto bad;
100594a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1006df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1007df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1008df8bae1dSRodney W. Grimes 					continue;
1009df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1010df8bae1dSRodney W. Grimes 				break;
1011df8bae1dSRodney W. Grimes 
1012df8bae1dSRodney W. Grimes 			default:
1013df8bae1dSRodney W. Grimes 				goto bad;
1014df8bae1dSRodney W. Grimes 			}
1015df8bae1dSRodney W. Grimes 			ntime = iptime();
101694a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1017df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1018df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1019df8bae1dSRodney W. Grimes 		}
1020df8bae1dSRodney W. Grimes 	}
1021df8bae1dSRodney W. Grimes 	if (forward) {
1022df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1023df8bae1dSRodney W. Grimes 		return (1);
1024df8bae1dSRodney W. Grimes 	}
1025df8bae1dSRodney W. Grimes 	return (0);
1026df8bae1dSRodney W. Grimes bad:
102758938916SGarrett Wollman 	ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2;   /* XXX icmp_error adds in hdr length */
1028df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1029df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1030df8bae1dSRodney W. Grimes 	return (1);
1031df8bae1dSRodney W. Grimes }
1032df8bae1dSRodney W. Grimes 
1033df8bae1dSRodney W. Grimes /*
1034df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1035df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1036df8bae1dSRodney W. Grimes  */
10370312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1038df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1039df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1040df8bae1dSRodney W. Grimes {
1041df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1042df8bae1dSRodney W. Grimes 
1043df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1044df8bae1dSRodney W. Grimes 
1045df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1046df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1047df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1048df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1049df8bae1dSRodney W. Grimes 		}
1050df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1051df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1052df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1053df8bae1dSRodney W. Grimes 
10542c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1055df8bae1dSRodney W. Grimes 	}
1056df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1057df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1058df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1059df8bae1dSRodney W. Grimes }
1060df8bae1dSRodney W. Grimes 
1061df8bae1dSRodney W. Grimes /*
1062df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1063df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1064df8bae1dSRodney W. Grimes  */
1065df8bae1dSRodney W. Grimes void
1066df8bae1dSRodney W. Grimes save_rte(option, dst)
1067df8bae1dSRodney W. Grimes 	u_char *option;
1068df8bae1dSRodney W. Grimes 	struct in_addr dst;
1069df8bae1dSRodney W. Grimes {
1070df8bae1dSRodney W. Grimes 	unsigned olen;
1071df8bae1dSRodney W. Grimes 
1072df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1073df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1074df8bae1dSRodney W. Grimes 	if (ipprintfs)
1075df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1076df8bae1dSRodney W. Grimes #endif
1077df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1078df8bae1dSRodney W. Grimes 		return;
10790453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1080df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1081df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1082df8bae1dSRodney W. Grimes }
1083df8bae1dSRodney W. Grimes 
1084df8bae1dSRodney W. Grimes /*
1085df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1086df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1087df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1088df8bae1dSRodney W. Grimes  */
1089df8bae1dSRodney W. Grimes struct mbuf *
1090df8bae1dSRodney W. Grimes ip_srcroute()
1091df8bae1dSRodney W. Grimes {
1092df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1093df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1094df8bae1dSRodney W. Grimes 
1095df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1096df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1097df8bae1dSRodney W. Grimes 	m = m_get(M_DONTWAIT, MT_SOOPTS);
1098df8bae1dSRodney W. Grimes 	if (m == 0)
1099df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1100df8bae1dSRodney W. Grimes 
1101df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1102df8bae1dSRodney W. Grimes 
1103df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1104df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1105df8bae1dSRodney W. Grimes 	    OPTSIZ;
1106df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1107df8bae1dSRodney W. Grimes 	if (ipprintfs)
1108df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1109df8bae1dSRodney W. Grimes #endif
1110df8bae1dSRodney W. Grimes 
1111df8bae1dSRodney W. Grimes 	/*
1112df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1113df8bae1dSRodney W. Grimes 	 */
1114df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1115df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1116df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1117df8bae1dSRodney W. Grimes 	if (ipprintfs)
1118df8bae1dSRodney W. Grimes 		printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
1119df8bae1dSRodney W. Grimes #endif
1120df8bae1dSRodney W. Grimes 
1121df8bae1dSRodney W. Grimes 	/*
1122df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1123df8bae1dSRodney W. Grimes 	 */
1124df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1125df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
112694a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
112794a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1128df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1129df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1130df8bae1dSRodney W. Grimes #undef OPTSIZ
1131df8bae1dSRodney W. Grimes 	/*
1132df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1133df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1134df8bae1dSRodney W. Grimes 	 */
1135df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1136df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1137df8bae1dSRodney W. Grimes 		if (ipprintfs)
1138df8bae1dSRodney W. Grimes 			printf(" %lx", ntohl(q->s_addr));
1139df8bae1dSRodney W. Grimes #endif
1140df8bae1dSRodney W. Grimes 		*q++ = *p--;
1141df8bae1dSRodney W. Grimes 	}
1142df8bae1dSRodney W. Grimes 	/*
1143df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1144df8bae1dSRodney W. Grimes 	 */
1145df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1146df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1147df8bae1dSRodney W. Grimes 	if (ipprintfs)
1148df8bae1dSRodney W. Grimes 		printf(" %lx\n", ntohl(q->s_addr));
1149df8bae1dSRodney W. Grimes #endif
1150df8bae1dSRodney W. Grimes 	return (m);
1151df8bae1dSRodney W. Grimes }
1152df8bae1dSRodney W. Grimes 
1153df8bae1dSRodney W. Grimes /*
1154df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1155df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1156df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1157df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1158df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1159df8bae1dSRodney W. Grimes  */
1160df8bae1dSRodney W. Grimes void
1161df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1162df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1163df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1164df8bae1dSRodney W. Grimes {
1165df8bae1dSRodney W. Grimes 	register int i;
1166df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1167df8bae1dSRodney W. Grimes 	register caddr_t opts;
1168df8bae1dSRodney W. Grimes 	int olen;
1169df8bae1dSRodney W. Grimes 
117058938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1171df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1172df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1173df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1174df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1175df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1176df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
117758938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1178df8bae1dSRodney W. Grimes }
1179df8bae1dSRodney W. Grimes 
1180df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1181df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1182df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1183df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1184df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1185df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1186df8bae1dSRodney W. Grimes 	ENOPROTOOPT
1187df8bae1dSRodney W. Grimes };
1188df8bae1dSRodney W. Grimes 
1189df8bae1dSRodney W. Grimes /*
1190df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1191df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1192df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1193df8bae1dSRodney W. Grimes  * of codes and types.
1194df8bae1dSRodney W. Grimes  *
1195df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1196df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1197df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1198df8bae1dSRodney W. Grimes  * protocol deal with that.
1199df8bae1dSRodney W. Grimes  *
1200df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1201df8bae1dSRodney W. Grimes  * via a source route.
1202df8bae1dSRodney W. Grimes  */
12030312fbe9SPoul-Henning Kamp static void
1204df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1205df8bae1dSRodney W. Grimes 	struct mbuf *m;
1206df8bae1dSRodney W. Grimes 	int srcrt;
1207df8bae1dSRodney W. Grimes {
1208df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1209df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1210df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
121126f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1212df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1213df8bae1dSRodney W. Grimes 	n_long dest;
1214df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
1215df8bae1dSRodney W. Grimes 
1216df8bae1dSRodney W. Grimes 	dest = 0;
1217df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1218df8bae1dSRodney W. Grimes 	if (ipprintfs)
121961ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1220623ae52eSPoul-Henning Kamp 			ip->ip_src.s_addr, ip->ip_dst.s_addr, ip->ip_ttl);
1221df8bae1dSRodney W. Grimes #endif
1222100ba1a6SJordan K. Hubbard 
1223100ba1a6SJordan K. Hubbard 
1224df8bae1dSRodney W. Grimes 	if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
1225df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1226df8bae1dSRodney W. Grimes 		m_freem(m);
1227df8bae1dSRodney W. Grimes 		return;
1228df8bae1dSRodney W. Grimes 	}
1229df8bae1dSRodney W. Grimes 	HTONS(ip->ip_id);
1230df8bae1dSRodney W. Grimes 	if (ip->ip_ttl <= IPTTLDEC) {
1231df8bae1dSRodney W. Grimes 		icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
1232df8bae1dSRodney W. Grimes 		return;
1233df8bae1dSRodney W. Grimes 	}
1234df8bae1dSRodney W. Grimes 	ip->ip_ttl -= IPTTLDEC;
1235df8bae1dSRodney W. Grimes 
1236df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1237df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1238df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1239df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1240df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1241df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1242df8bae1dSRodney W. Grimes 		}
1243df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1244df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1245df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1246df8bae1dSRodney W. Grimes 
12472c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1248df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1249df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1250df8bae1dSRodney W. Grimes 			return;
1251df8bae1dSRodney W. Grimes 		}
1252df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1253df8bae1dSRodney W. Grimes 	}
1254df8bae1dSRodney W. Grimes 
1255df8bae1dSRodney W. Grimes 	/*
1256df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1257df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1258df8bae1dSRodney W. Grimes 	 */
1259df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
1260df8bae1dSRodney W. Grimes 
1261df8bae1dSRodney W. Grimes 	/*
1262df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1263df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1264df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1265df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1266df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1267df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1268df8bae1dSRodney W. Grimes 	 */
1269df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1270df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1271df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1272df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1273df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1274df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1275df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1276df8bae1dSRodney W. Grimes 
1277df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1278df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1279df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1280df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1281df8bae1dSRodney W. Grimes 		    else
1282df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1283df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1284df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1285df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1286df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1287df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1288df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1289df8bae1dSRodney W. Grimes #endif
1290df8bae1dSRodney W. Grimes 		}
1291df8bae1dSRodney W. Grimes 	}
1292df8bae1dSRodney W. Grimes 
1293b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1294b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1295df8bae1dSRodney W. Grimes 	if (error)
1296df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1297df8bae1dSRodney W. Grimes 	else {
1298df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1299df8bae1dSRodney W. Grimes 		if (type)
1300df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1301df8bae1dSRodney W. Grimes 		else {
1302df8bae1dSRodney W. Grimes 			if (mcopy)
1303df8bae1dSRodney W. Grimes 				m_freem(mcopy);
1304df8bae1dSRodney W. Grimes 			return;
1305df8bae1dSRodney W. Grimes 		}
1306df8bae1dSRodney W. Grimes 	}
1307df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1308df8bae1dSRodney W. Grimes 		return;
1309df8bae1dSRodney W. Grimes 	destifp = NULL;
1310df8bae1dSRodney W. Grimes 
1311df8bae1dSRodney W. Grimes 	switch (error) {
1312df8bae1dSRodney W. Grimes 
1313df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1314df8bae1dSRodney W. Grimes 		/* type, code set above */
1315df8bae1dSRodney W. Grimes 		break;
1316df8bae1dSRodney W. Grimes 
1317df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1318df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1319df8bae1dSRodney W. Grimes 	case ENETDOWN:
1320df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1321df8bae1dSRodney W. Grimes 	default:
1322df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1323df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1324df8bae1dSRodney W. Grimes 		break;
1325df8bae1dSRodney W. Grimes 
1326df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1327df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1328df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
1329df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1330df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
1331df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1332df8bae1dSRodney W. Grimes 		break;
1333df8bae1dSRodney W. Grimes 
1334df8bae1dSRodney W. Grimes 	case ENOBUFS:
1335df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1336df8bae1dSRodney W. Grimes 		code = 0;
1337df8bae1dSRodney W. Grimes 		break;
1338df8bae1dSRodney W. Grimes 	}
1339df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1340df8bae1dSRodney W. Grimes }
1341df8bae1dSRodney W. Grimes 
134282c23ebaSBill Fenner void
134382c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
134482c23ebaSBill Fenner 	register struct inpcb *inp;
134582c23ebaSBill Fenner 	register struct mbuf **mp;
134682c23ebaSBill Fenner 	register struct ip *ip;
134782c23ebaSBill Fenner 	register struct mbuf *m;
134882c23ebaSBill Fenner {
134982c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
135082c23ebaSBill Fenner 		struct timeval tv;
135182c23ebaSBill Fenner 
135282c23ebaSBill Fenner 		microtime(&tv);
135382c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
135482c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
135582c23ebaSBill Fenner 		if (*mp)
135682c23ebaSBill Fenner 			mp = &(*mp)->m_next;
135782c23ebaSBill Fenner 	}
135882c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
135982c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
136082c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
136182c23ebaSBill Fenner 		if (*mp)
136282c23ebaSBill Fenner 			mp = &(*mp)->m_next;
136382c23ebaSBill Fenner 	}
136482c23ebaSBill Fenner #ifdef notyet
136582c23ebaSBill Fenner 	/* XXX
136682c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
136782c23ebaSBill Fenner 	 * than they already were.
136882c23ebaSBill Fenner 	 */
136982c23ebaSBill Fenner 	/* options were tossed already */
137082c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
137182c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
137282c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
137382c23ebaSBill Fenner 		if (*mp)
137482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
137582c23ebaSBill Fenner 	}
137682c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
137782c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
137882c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
137982c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
138082c23ebaSBill Fenner 		if (*mp)
138182c23ebaSBill Fenner 			mp = &(*mp)->m_next;
138282c23ebaSBill Fenner 	}
138382c23ebaSBill Fenner #endif
138482c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
138582c23ebaSBill Fenner 		struct sockaddr_dl sdl;
138682c23ebaSBill Fenner 
138782c23ebaSBill Fenner 		sdl.sdl_len = offsetof(struct sockaddr_dl, sdl_data[0]);
138882c23ebaSBill Fenner 		sdl.sdl_family = AF_LINK;
138982c23ebaSBill Fenner 		sdl.sdl_index = m->m_pkthdr.rcvif ?
139082c23ebaSBill Fenner 			m->m_pkthdr.rcvif->if_index : 0;
139182c23ebaSBill Fenner 		sdl.sdl_nlen = sdl.sdl_alen = sdl.sdl_slen = 0;
139282c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &sdl, sdl.sdl_len,
139382c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
139482c23ebaSBill Fenner 		if (*mp)
139582c23ebaSBill Fenner 			mp = &(*mp)->m_next;
139682c23ebaSBill Fenner 	}
139782c23ebaSBill Fenner }
139882c23ebaSBill Fenner 
1399df8bae1dSRodney W. Grimes int
1400f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1401f0068c4aSGarrett Wollman {
1402f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1403f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1404f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1405f0068c4aSGarrett Wollman 
1406f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1407f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1408f0068c4aSGarrett Wollman 
1409f0068c4aSGarrett Wollman 	ip_rsvpd = so;
14101c5de19aSGarrett Wollman 	/*
14111c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
14121c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
14131c5de19aSGarrett Wollman 	 */
14141c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
14151c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
14161c5de19aSGarrett Wollman 		rsvp_on++;
14171c5de19aSGarrett Wollman 	}
1418f0068c4aSGarrett Wollman 
1419f0068c4aSGarrett Wollman 	return 0;
1420f0068c4aSGarrett Wollman }
1421f0068c4aSGarrett Wollman 
1422f0068c4aSGarrett Wollman int
1423f0068c4aSGarrett Wollman ip_rsvp_done(void)
1424f0068c4aSGarrett Wollman {
1425f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
14261c5de19aSGarrett Wollman 	/*
14271c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
14281c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
14291c5de19aSGarrett Wollman 	 */
14301c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
14311c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
14321c5de19aSGarrett Wollman 		rsvp_on--;
14331c5de19aSGarrett Wollman 	}
1434f0068c4aSGarrett Wollman 	return 0;
1435f0068c4aSGarrett Wollman }
1436