xref: /freebsd/sys/netinet/ip_input.c (revision b5e8ce9f12b66453759f254bdf8bfc53fe2b6948)
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
34b5e8ce9fSBruce Evans  * $Id: ip_input.c,v 1.17 1995/02/14 23:04:52 wollman Exp $
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
37df8bae1dSRodney W. Grimes #include <sys/param.h>
38df8bae1dSRodney W. Grimes #include <sys/systm.h>
39df8bae1dSRodney W. Grimes #include <sys/malloc.h>
40df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
41df8bae1dSRodney W. Grimes #include <sys/domain.h>
42df8bae1dSRodney W. Grimes #include <sys/protosw.h>
43df8bae1dSRodney W. Grimes #include <sys/socket.h>
44df8bae1dSRodney W. Grimes #include <sys/errno.h>
45df8bae1dSRodney W. Grimes #include <sys/time.h>
46df8bae1dSRodney W. Grimes #include <sys/kernel.h>
47b5e8ce9fSBruce Evans #include <vm/vm.h>
48b5e8ce9fSBruce Evans #include <sys/sysctl.h>
49df8bae1dSRodney W. Grimes 
50df8bae1dSRodney W. Grimes #include <net/if.h>
51df8bae1dSRodney W. Grimes #include <net/route.h>
52df8bae1dSRodney W. Grimes 
53df8bae1dSRodney W. Grimes #include <netinet/in.h>
54df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
55b5e8ce9fSBruce Evans #include <netinet/in_var.h>
56df8bae1dSRodney W. Grimes #include <netinet/ip.h>
57df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
58df8bae1dSRodney W. Grimes #include <netinet/in_var.h>
59df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
60df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
61df8bae1dSRodney W. Grimes 
62100ba1a6SJordan K. Hubbard #include <netinet/ip_fw.h>
63100ba1a6SJordan K. Hubbard 
64f0068c4aSGarrett Wollman #include <sys/socketvar.h>
65f0068c4aSGarrett Wollman struct socket *ip_rsvpd;
66f0068c4aSGarrett Wollman 
67df8bae1dSRodney W. Grimes #ifndef	IPFORWARDING
68df8bae1dSRodney W. Grimes #ifdef GATEWAY
69df8bae1dSRodney W. Grimes #define	IPFORWARDING	1	/* forward IP packets not for us */
70df8bae1dSRodney W. Grimes #else /* GATEWAY */
71df8bae1dSRodney W. Grimes #define	IPFORWARDING	0	/* don't forward IP packets not for us */
72df8bae1dSRodney W. Grimes #endif /* GATEWAY */
73df8bae1dSRodney W. Grimes #endif /* IPFORWARDING */
74df8bae1dSRodney W. Grimes #ifndef	IPSENDREDIRECTS
75df8bae1dSRodney W. Grimes #define	IPSENDREDIRECTS	1
76df8bae1dSRodney W. Grimes #endif
77df8bae1dSRodney W. Grimes int	ipforwarding = IPFORWARDING;
78df8bae1dSRodney W. Grimes int	ipsendredirects = IPSENDREDIRECTS;
79df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
80df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
81df8bae1dSRodney W. Grimes int	ipprintfs = 0;
82df8bae1dSRodney W. Grimes #endif
83df8bae1dSRodney W. Grimes 
84df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
85df8bae1dSRodney W. Grimes extern	struct protosw inetsw[];
86df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
87df8bae1dSRodney W. Grimes int	ipqmaxlen = IFQ_MAXLEN;
88df8bae1dSRodney W. Grimes struct	in_ifaddr *in_ifaddr;			/* first inet address */
89df8bae1dSRodney W. Grimes struct	ifqueue ipintrq;
90df8bae1dSRodney W. Grimes 
91f23b4c91SGarrett Wollman struct ipstat ipstat;
92f23b4c91SGarrett Wollman struct ipq ipq;
93f23b4c91SGarrett Wollman 
94df8bae1dSRodney W. Grimes /*
95df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
96df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
97df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
98df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
99df8bae1dSRodney W. Grimes  * to us.
100df8bae1dSRodney W. Grimes  */
101df8bae1dSRodney W. Grimes int	ip_nhops = 0;
102df8bae1dSRodney W. Grimes static	struct ip_srcrt {
103df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
104df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
105df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
106df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
107df8bae1dSRodney W. Grimes } ip_srcrt;
108df8bae1dSRodney W. Grimes 
109df8bae1dSRodney W. Grimes #ifdef GATEWAY
110df8bae1dSRodney W. Grimes extern	int if_index;
111df8bae1dSRodney W. Grimes u_long	*ip_ifmatrix;
112df8bae1dSRodney W. Grimes #endif
113df8bae1dSRodney W. Grimes 
114df8bae1dSRodney W. Grimes static void save_rte __P((u_char *, struct in_addr));
115df8bae1dSRodney W. Grimes /*
116df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
117df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
118df8bae1dSRodney W. Grimes  */
119df8bae1dSRodney W. Grimes void
120df8bae1dSRodney W. Grimes ip_init()
121df8bae1dSRodney W. Grimes {
122df8bae1dSRodney W. Grimes 	register struct protosw *pr;
123df8bae1dSRodney W. Grimes 	register int i;
124df8bae1dSRodney W. Grimes 
125df8bae1dSRodney W. Grimes 	pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
126df8bae1dSRodney W. Grimes 	if (pr == 0)
127df8bae1dSRodney W. Grimes 		panic("ip_init");
128df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
129df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
130df8bae1dSRodney W. Grimes 	for (pr = inetdomain.dom_protosw;
131df8bae1dSRodney W. Grimes 	    pr < inetdomain.dom_protoswNPROTOSW; pr++)
132df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
133df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
134df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
135df8bae1dSRodney W. Grimes 	ipq.next = ipq.prev = &ipq;
136df8bae1dSRodney W. Grimes 	ip_id = time.tv_sec & 0xffff;
137df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
138df8bae1dSRodney W. Grimes #ifdef GATEWAY
139df8bae1dSRodney W. Grimes 	i = (if_index + 1) * (if_index + 1) * sizeof (u_long);
140df8bae1dSRodney W. Grimes 	ip_ifmatrix = (u_long *) malloc(i, M_RTABLE, M_WAITOK);
141df8bae1dSRodney W. Grimes 	bzero((char *)ip_ifmatrix, i);
142df8bae1dSRodney W. Grimes #endif
143df8bae1dSRodney W. Grimes }
144df8bae1dSRodney W. Grimes 
145df8bae1dSRodney W. Grimes struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
146df8bae1dSRodney W. Grimes struct	route ipforward_rt;
147df8bae1dSRodney W. Grimes 
148df8bae1dSRodney W. Grimes /*
149df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
150df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
151df8bae1dSRodney W. Grimes  */
152df8bae1dSRodney W. Grimes void
153df8bae1dSRodney W. Grimes ipintr()
154df8bae1dSRodney W. Grimes {
155df8bae1dSRodney W. Grimes 	register struct ip *ip;
156df8bae1dSRodney W. Grimes 	register struct mbuf *m;
157df8bae1dSRodney W. Grimes 	register struct ipq *fp;
158df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
159df8bae1dSRodney W. Grimes 	int hlen, s;
160df8bae1dSRodney W. Grimes 
161df8bae1dSRodney W. Grimes next:
162df8bae1dSRodney W. Grimes 	/*
163df8bae1dSRodney W. Grimes 	 * Get next datagram off input queue and get IP header
164df8bae1dSRodney W. Grimes 	 * in first mbuf.
165df8bae1dSRodney W. Grimes 	 */
166df8bae1dSRodney W. Grimes 	s = splimp();
167df8bae1dSRodney W. Grimes 	IF_DEQUEUE(&ipintrq, m);
168df8bae1dSRodney W. Grimes 	splx(s);
169df8bae1dSRodney W. Grimes 	if (m == 0)
170df8bae1dSRodney W. Grimes 		return;
171df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
172df8bae1dSRodney W. Grimes 	if ((m->m_flags & M_PKTHDR) == 0)
173df8bae1dSRodney W. Grimes 		panic("ipintr no HDR");
174df8bae1dSRodney W. Grimes #endif
175df8bae1dSRodney W. Grimes 	/*
176df8bae1dSRodney W. Grimes 	 * If no IP addresses have been set yet but the interfaces
177df8bae1dSRodney W. Grimes 	 * are receiving, can't do anything with incoming packets yet.
178df8bae1dSRodney W. Grimes 	 */
179df8bae1dSRodney W. Grimes 	if (in_ifaddr == NULL)
180df8bae1dSRodney W. Grimes 		goto bad;
181df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
182df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
183df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
184df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
185df8bae1dSRodney W. Grimes 		goto next;
186df8bae1dSRodney W. Grimes 	}
187df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
188df8bae1dSRodney W. Grimes 	if (ip->ip_v != IPVERSION) {
189df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
190df8bae1dSRodney W. Grimes 		goto bad;
191df8bae1dSRodney W. Grimes 	}
192df8bae1dSRodney W. Grimes 	hlen = ip->ip_hl << 2;
193df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
194df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
195df8bae1dSRodney W. Grimes 		goto bad;
196df8bae1dSRodney W. Grimes 	}
197df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
198df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
199df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
200df8bae1dSRodney W. Grimes 			goto next;
201df8bae1dSRodney W. Grimes 		}
202df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
203df8bae1dSRodney W. Grimes 	}
204623ae52eSPoul-Henning Kamp 	ip->ip_sum = in_cksum(m, hlen);
205623ae52eSPoul-Henning Kamp 	if (ip->ip_sum) {
206df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
207df8bae1dSRodney W. Grimes 		goto bad;
208df8bae1dSRodney W. Grimes 	}
209df8bae1dSRodney W. Grimes 
210df8bae1dSRodney W. Grimes 	/*
211df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
212df8bae1dSRodney W. Grimes 	 */
213df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
214df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
215df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
216df8bae1dSRodney W. Grimes 		goto bad;
217df8bae1dSRodney W. Grimes 	}
218df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_id);
219df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
220df8bae1dSRodney W. Grimes 
221df8bae1dSRodney W. Grimes 	/*
222df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
223df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
224df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
225df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
226df8bae1dSRodney W. Grimes 	 */
227df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
228df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
229df8bae1dSRodney W. Grimes 		goto bad;
230df8bae1dSRodney W. Grimes 	}
231df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
232df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
233df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
234df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
235df8bae1dSRodney W. Grimes 		} else
236df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
237df8bae1dSRodney W. Grimes 	}
2384dd1662bSUgen J.S. Antsilevich 	/*
2394dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
2404dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
2414dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
2424dd1662bSUgen J.S. Antsilevich 	 * deals with it.
2434dd1662bSUgen J.S. Antsilevich 	 * - Firewall: deny/allow
2444dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
2454dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
2464dd1662bSUgen J.S. Antsilevich  	 */
247df8bae1dSRodney W. Grimes 
2484dd1662bSUgen J.S. Antsilevich         if (ip_fw_chk_ptr!=NULL)
2496db216a6SGary Palmer                if (!(*ip_fw_chk_ptr)(ip,m->m_pkthdr.rcvif,ip_fw_chain) ) {
250100ba1a6SJordan K. Hubbard                        goto bad;
251100ba1a6SJordan K. Hubbard                }
252100ba1a6SJordan K. Hubbard 
253df8bae1dSRodney W. Grimes 	/*
254df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
255df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
256df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
257df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
258df8bae1dSRodney W. Grimes 	 */
259df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
260df8bae1dSRodney W. Grimes 	if (hlen > sizeof (struct ip) && ip_dooptions(m))
261df8bae1dSRodney W. Grimes 		goto next;
262df8bae1dSRodney W. Grimes 
263f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
264f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
265f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
266f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
267f0068c4aSGarrett Wollman 	 * grabbing the packet.
268f0068c4aSGarrett Wollman          */
269f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL && ip->ip_p==IPPROTO_RSVP)
270f0068c4aSGarrett Wollman 		goto ours;
271f0068c4aSGarrett Wollman 
272df8bae1dSRodney W. Grimes 	/*
273df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
274df8bae1dSRodney W. Grimes 	 */
275df8bae1dSRodney W. Grimes 	for (ia = in_ifaddr; ia; ia = ia->ia_next) {
276df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
277df8bae1dSRodney W. Grimes 
278df8bae1dSRodney W. Grimes 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
279df8bae1dSRodney W. Grimes 			goto ours;
280df8bae1dSRodney W. Grimes 		if (
281df8bae1dSRodney W. Grimes #ifdef	DIRECTED_BROADCAST
282df8bae1dSRodney W. Grimes 		    ia->ia_ifp == m->m_pkthdr.rcvif &&
283df8bae1dSRodney W. Grimes #endif
284df8bae1dSRodney W. Grimes 		    (ia->ia_ifp->if_flags & IFF_BROADCAST)) {
285df8bae1dSRodney W. Grimes 			u_long t;
286df8bae1dSRodney W. Grimes 
287df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
288df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr)
289df8bae1dSRodney W. Grimes 				goto ours;
290df8bae1dSRodney W. Grimes 			if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr)
291df8bae1dSRodney W. Grimes 				goto ours;
292df8bae1dSRodney W. Grimes 			/*
293df8bae1dSRodney W. Grimes 			 * Look for all-0's host part (old broadcast addr),
294df8bae1dSRodney W. Grimes 			 * either for subnet or net.
295df8bae1dSRodney W. Grimes 			 */
296df8bae1dSRodney W. Grimes 			t = ntohl(ip->ip_dst.s_addr);
297df8bae1dSRodney W. Grimes 			if (t == ia->ia_subnet)
298df8bae1dSRodney W. Grimes 				goto ours;
299df8bae1dSRodney W. Grimes 			if (t == ia->ia_net)
300df8bae1dSRodney W. Grimes 				goto ours;
301df8bae1dSRodney W. Grimes 		}
302df8bae1dSRodney W. Grimes 	}
303df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
304df8bae1dSRodney W. Grimes 		struct in_multi *inm;
305df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
306df8bae1dSRodney W. Grimes 			/*
307df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
308df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
309df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
310df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
311df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
312df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
313df8bae1dSRodney W. Grimes 			 *
314df8bae1dSRodney W. Grimes 			 * (The IP ident field is put in the same byte order
315df8bae1dSRodney W. Grimes 			 * as expected when ip_mforward() is called from
316df8bae1dSRodney W. Grimes 			 * ip_output().)
317df8bae1dSRodney W. Grimes 			 */
318df8bae1dSRodney W. Grimes 			ip->ip_id = htons(ip->ip_id);
319f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
320df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
321df8bae1dSRodney W. Grimes 				m_freem(m);
322df8bae1dSRodney W. Grimes 				goto next;
323df8bae1dSRodney W. Grimes 			}
324df8bae1dSRodney W. Grimes 			ip->ip_id = ntohs(ip->ip_id);
325df8bae1dSRodney W. Grimes 
326df8bae1dSRodney W. Grimes 			/*
327df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
328df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
329df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
330df8bae1dSRodney W. Grimes 			 */
331df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
332df8bae1dSRodney W. Grimes 				goto ours;
333df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
334df8bae1dSRodney W. Grimes 		}
335df8bae1dSRodney W. Grimes 		/*
336df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
337df8bae1dSRodney W. Grimes 		 * arrival interface.
338df8bae1dSRodney W. Grimes 		 */
339df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
340df8bae1dSRodney W. Grimes 		if (inm == NULL) {
341df8bae1dSRodney W. Grimes 			ipstat.ips_cantforward++;
342df8bae1dSRodney W. Grimes 			m_freem(m);
343df8bae1dSRodney W. Grimes 			goto next;
344df8bae1dSRodney W. Grimes 		}
345df8bae1dSRodney W. Grimes 		goto ours;
346df8bae1dSRodney W. Grimes 	}
347df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
348df8bae1dSRodney W. Grimes 		goto ours;
349df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
350df8bae1dSRodney W. Grimes 		goto ours;
351df8bae1dSRodney W. Grimes 
352df8bae1dSRodney W. Grimes 	/*
353df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
354df8bae1dSRodney W. Grimes 	 */
355df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
356df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
357df8bae1dSRodney W. Grimes 		m_freem(m);
358df8bae1dSRodney W. Grimes 	} else
359df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
360df8bae1dSRodney W. Grimes 	goto next;
361df8bae1dSRodney W. Grimes 
362df8bae1dSRodney W. Grimes ours:
363100ba1a6SJordan K. Hubbard 
36463f8d699SJordan K. Hubbard 		/*
36563f8d699SJordan K. Hubbard 		 * If packet came to us we count it...
36663f8d699SJordan K. Hubbard 		 * This way we count all incoming packets which has
36763f8d699SJordan K. Hubbard 		 * not been forwarded...
36863f8d699SJordan K. Hubbard 		 * Do not convert ip_len to host byte order when
36963f8d699SJordan K. Hubbard 		 * counting,ppl already made it for us before..
37063f8d699SJordan K. Hubbard 		 */
3714dd1662bSUgen J.S. Antsilevich 	if (ip_acct_cnt_ptr!=NULL)
3724dd1662bSUgen J.S. Antsilevich 		(*ip_acct_cnt_ptr)(ip,m->m_pkthdr.rcvif,ip_acct_chain,0);
37363f8d699SJordan K. Hubbard 
374df8bae1dSRodney W. Grimes 	/*
375df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
376df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
377df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
378df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
379df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
380df8bae1dSRodney W. Grimes 	 */
381df8bae1dSRodney W. Grimes 	if (ip->ip_off &~ IP_DF) {
382df8bae1dSRodney W. Grimes 		if (m->m_flags & M_EXT) {		/* XXX */
383df8bae1dSRodney W. Grimes 			if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
384df8bae1dSRodney W. Grimes 				ipstat.ips_toosmall++;
385df8bae1dSRodney W. Grimes 				goto next;
386df8bae1dSRodney W. Grimes 			}
387df8bae1dSRodney W. Grimes 			ip = mtod(m, struct ip *);
388df8bae1dSRodney W. Grimes 		}
389df8bae1dSRodney W. Grimes 		/*
390df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
391df8bae1dSRodney W. Grimes 		 * of this datagram.
392df8bae1dSRodney W. Grimes 		 */
393df8bae1dSRodney W. Grimes 		for (fp = ipq.next; fp != &ipq; fp = fp->next)
394df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
395df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
396df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
397df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
398df8bae1dSRodney W. Grimes 				goto found;
399df8bae1dSRodney W. Grimes 		fp = 0;
400df8bae1dSRodney W. Grimes found:
401df8bae1dSRodney W. Grimes 
402df8bae1dSRodney W. Grimes 		/*
403df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
404df8bae1dSRodney W. Grimes 		 * set ip_mff if more fragments are expected,
405df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
406df8bae1dSRodney W. Grimes 		 */
407df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
408df8bae1dSRodney W. Grimes 		((struct ipasfrag *)ip)->ipf_mff &= ~1;
409df8bae1dSRodney W. Grimes 		if (ip->ip_off & IP_MF)
410df8bae1dSRodney W. Grimes 			((struct ipasfrag *)ip)->ipf_mff |= 1;
411df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
412df8bae1dSRodney W. Grimes 
413df8bae1dSRodney W. Grimes 		/*
414df8bae1dSRodney W. Grimes 		 * If datagram marked as having more fragments
415df8bae1dSRodney W. Grimes 		 * or if this is not the first fragment,
416df8bae1dSRodney W. Grimes 		 * attempt reassembly; if it succeeds, proceed.
417df8bae1dSRodney W. Grimes 		 */
418df8bae1dSRodney W. Grimes 		if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
419df8bae1dSRodney W. Grimes 			ipstat.ips_fragments++;
420df8bae1dSRodney W. Grimes 			ip = ip_reass((struct ipasfrag *)ip, fp);
421df8bae1dSRodney W. Grimes 			if (ip == 0)
422df8bae1dSRodney W. Grimes 				goto next;
423df8bae1dSRodney W. Grimes 			ipstat.ips_reassembled++;
424df8bae1dSRodney W. Grimes 			m = dtom(ip);
425df8bae1dSRodney W. Grimes 		} else
426df8bae1dSRodney W. Grimes 			if (fp)
427df8bae1dSRodney W. Grimes 				ip_freef(fp);
428df8bae1dSRodney W. Grimes 	} else
429df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
430df8bae1dSRodney W. Grimes 
431df8bae1dSRodney W. Grimes 	/*
432df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
433df8bae1dSRodney W. Grimes 	 */
434df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
435df8bae1dSRodney W. Grimes 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
436df8bae1dSRodney W. Grimes 	goto next;
437df8bae1dSRodney W. Grimes bad:
438df8bae1dSRodney W. Grimes 	m_freem(m);
439df8bae1dSRodney W. Grimes 	goto next;
440df8bae1dSRodney W. Grimes }
441df8bae1dSRodney W. Grimes 
442df8bae1dSRodney W. Grimes /*
443df8bae1dSRodney W. Grimes  * Take incoming datagram fragment and try to
444df8bae1dSRodney W. Grimes  * reassemble it into whole datagram.  If a chain for
445df8bae1dSRodney W. Grimes  * reassembly of this datagram already exists, then it
446df8bae1dSRodney W. Grimes  * is given as fp; otherwise have to make a chain.
447df8bae1dSRodney W. Grimes  */
448df8bae1dSRodney W. Grimes struct ip *
449df8bae1dSRodney W. Grimes ip_reass(ip, fp)
450df8bae1dSRodney W. Grimes 	register struct ipasfrag *ip;
451df8bae1dSRodney W. Grimes 	register struct ipq *fp;
452df8bae1dSRodney W. Grimes {
453df8bae1dSRodney W. Grimes 	register struct mbuf *m = dtom(ip);
454df8bae1dSRodney W. Grimes 	register struct ipasfrag *q;
455df8bae1dSRodney W. Grimes 	struct mbuf *t;
456df8bae1dSRodney W. Grimes 	int hlen = ip->ip_hl << 2;
457df8bae1dSRodney W. Grimes 	int i, next;
458df8bae1dSRodney W. Grimes 
459df8bae1dSRodney W. Grimes 	/*
460df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
461df8bae1dSRodney W. Grimes 	 * would confuse code below.
462df8bae1dSRodney W. Grimes 	 */
463df8bae1dSRodney W. Grimes 	m->m_data += hlen;
464df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
465df8bae1dSRodney W. Grimes 
466df8bae1dSRodney W. Grimes 	/*
467df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
468df8bae1dSRodney W. Grimes 	 */
469df8bae1dSRodney W. Grimes 	if (fp == 0) {
470df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
471df8bae1dSRodney W. Grimes 			goto dropfrag;
472df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
473df8bae1dSRodney W. Grimes 		insque(fp, &ipq);
474df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
475df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
476df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
477df8bae1dSRodney W. Grimes 		fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
478df8bae1dSRodney W. Grimes 		fp->ipq_src = ((struct ip *)ip)->ip_src;
479df8bae1dSRodney W. Grimes 		fp->ipq_dst = ((struct ip *)ip)->ip_dst;
480df8bae1dSRodney W. Grimes 		q = (struct ipasfrag *)fp;
481df8bae1dSRodney W. Grimes 		goto insert;
482df8bae1dSRodney W. Grimes 	}
483df8bae1dSRodney W. Grimes 
484df8bae1dSRodney W. Grimes 	/*
485df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
486df8bae1dSRodney W. Grimes 	 */
487df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next)
488df8bae1dSRodney W. Grimes 		if (q->ip_off > ip->ip_off)
489df8bae1dSRodney W. Grimes 			break;
490df8bae1dSRodney W. Grimes 
491df8bae1dSRodney W. Grimes 	/*
492df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
493df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
494df8bae1dSRodney W. Grimes 	 * segment.  If it provides all of our data, drop us.
495df8bae1dSRodney W. Grimes 	 */
496df8bae1dSRodney W. Grimes 	if (q->ipf_prev != (struct ipasfrag *)fp) {
497df8bae1dSRodney W. Grimes 		i = q->ipf_prev->ip_off + q->ipf_prev->ip_len - ip->ip_off;
498df8bae1dSRodney W. Grimes 		if (i > 0) {
499df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
500df8bae1dSRodney W. Grimes 				goto dropfrag;
501df8bae1dSRodney W. Grimes 			m_adj(dtom(ip), i);
502df8bae1dSRodney W. Grimes 			ip->ip_off += i;
503df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
504df8bae1dSRodney W. Grimes 		}
505df8bae1dSRodney W. Grimes 	}
506df8bae1dSRodney W. Grimes 
507df8bae1dSRodney W. Grimes 	/*
508df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
509df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
510df8bae1dSRodney W. Grimes 	 */
511df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) {
512df8bae1dSRodney W. Grimes 		i = (ip->ip_off + ip->ip_len) - q->ip_off;
513df8bae1dSRodney W. Grimes 		if (i < q->ip_len) {
514df8bae1dSRodney W. Grimes 			q->ip_len -= i;
515df8bae1dSRodney W. Grimes 			q->ip_off += i;
516df8bae1dSRodney W. Grimes 			m_adj(dtom(q), i);
517df8bae1dSRodney W. Grimes 			break;
518df8bae1dSRodney W. Grimes 		}
519df8bae1dSRodney W. Grimes 		q = q->ipf_next;
520df8bae1dSRodney W. Grimes 		m_freem(dtom(q->ipf_prev));
521df8bae1dSRodney W. Grimes 		ip_deq(q->ipf_prev);
522df8bae1dSRodney W. Grimes 	}
523df8bae1dSRodney W. Grimes 
524df8bae1dSRodney W. Grimes insert:
525df8bae1dSRodney W. Grimes 	/*
526df8bae1dSRodney W. Grimes 	 * Stick new segment in its place;
527df8bae1dSRodney W. Grimes 	 * check for complete reassembly.
528df8bae1dSRodney W. Grimes 	 */
529df8bae1dSRodney W. Grimes 	ip_enq(ip, q->ipf_prev);
530df8bae1dSRodney W. Grimes 	next = 0;
531df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = q->ipf_next) {
532df8bae1dSRodney W. Grimes 		if (q->ip_off != next)
533df8bae1dSRodney W. Grimes 			return (0);
534df8bae1dSRodney W. Grimes 		next += q->ip_len;
535df8bae1dSRodney W. Grimes 	}
536df8bae1dSRodney W. Grimes 	if (q->ipf_prev->ipf_mff & 1)
537df8bae1dSRodney W. Grimes 		return (0);
538df8bae1dSRodney W. Grimes 
539df8bae1dSRodney W. Grimes 	/*
540df8bae1dSRodney W. Grimes 	 * Reassembly is complete; concatenate fragments.
541df8bae1dSRodney W. Grimes 	 */
542df8bae1dSRodney W. Grimes 	q = fp->ipq_next;
543df8bae1dSRodney W. Grimes 	m = dtom(q);
544df8bae1dSRodney W. Grimes 	t = m->m_next;
545df8bae1dSRodney W. Grimes 	m->m_next = 0;
546df8bae1dSRodney W. Grimes 	m_cat(m, t);
547df8bae1dSRodney W. Grimes 	q = q->ipf_next;
548df8bae1dSRodney W. Grimes 	while (q != (struct ipasfrag *)fp) {
549df8bae1dSRodney W. Grimes 		t = dtom(q);
550df8bae1dSRodney W. Grimes 		q = q->ipf_next;
551df8bae1dSRodney W. Grimes 		m_cat(m, t);
552df8bae1dSRodney W. Grimes 	}
553df8bae1dSRodney W. Grimes 
554df8bae1dSRodney W. Grimes 	/*
555df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
556df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
557df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
558df8bae1dSRodney W. Grimes 	 * Make header visible.
559df8bae1dSRodney W. Grimes 	 */
560df8bae1dSRodney W. Grimes 	ip = fp->ipq_next;
561df8bae1dSRodney W. Grimes 	ip->ip_len = next;
562df8bae1dSRodney W. Grimes 	ip->ipf_mff &= ~1;
563df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_src = fp->ipq_src;
564df8bae1dSRodney W. Grimes 	((struct ip *)ip)->ip_dst = fp->ipq_dst;
565df8bae1dSRodney W. Grimes 	remque(fp);
566df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
567df8bae1dSRodney W. Grimes 	m = dtom(ip);
568df8bae1dSRodney W. Grimes 	m->m_len += (ip->ip_hl << 2);
569df8bae1dSRodney W. Grimes 	m->m_data -= (ip->ip_hl << 2);
570df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
571df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
572df8bae1dSRodney W. Grimes 		register int plen = 0;
573df8bae1dSRodney W. Grimes 		for (t = m; m; m = m->m_next)
574df8bae1dSRodney W. Grimes 			plen += m->m_len;
575df8bae1dSRodney W. Grimes 		t->m_pkthdr.len = plen;
576df8bae1dSRodney W. Grimes 	}
577df8bae1dSRodney W. Grimes 	return ((struct ip *)ip);
578df8bae1dSRodney W. Grimes 
579df8bae1dSRodney W. Grimes dropfrag:
580df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
581df8bae1dSRodney W. Grimes 	m_freem(m);
582df8bae1dSRodney W. Grimes 	return (0);
583df8bae1dSRodney W. Grimes }
584df8bae1dSRodney W. Grimes 
585df8bae1dSRodney W. Grimes /*
586df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
587df8bae1dSRodney W. Grimes  * associated datagrams.
588df8bae1dSRodney W. Grimes  */
589df8bae1dSRodney W. Grimes void
590df8bae1dSRodney W. Grimes ip_freef(fp)
591df8bae1dSRodney W. Grimes 	struct ipq *fp;
592df8bae1dSRodney W. Grimes {
593df8bae1dSRodney W. Grimes 	register struct ipasfrag *q, *p;
594df8bae1dSRodney W. Grimes 
595df8bae1dSRodney W. Grimes 	for (q = fp->ipq_next; q != (struct ipasfrag *)fp; q = p) {
596df8bae1dSRodney W. Grimes 		p = q->ipf_next;
597df8bae1dSRodney W. Grimes 		ip_deq(q);
598df8bae1dSRodney W. Grimes 		m_freem(dtom(q));
599df8bae1dSRodney W. Grimes 	}
600df8bae1dSRodney W. Grimes 	remque(fp);
601df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
602df8bae1dSRodney W. Grimes }
603df8bae1dSRodney W. Grimes 
604df8bae1dSRodney W. Grimes /*
605df8bae1dSRodney W. Grimes  * Put an ip fragment on a reassembly chain.
606df8bae1dSRodney W. Grimes  * Like insque, but pointers in middle of structure.
607df8bae1dSRodney W. Grimes  */
608df8bae1dSRodney W. Grimes void
609df8bae1dSRodney W. Grimes ip_enq(p, prev)
610df8bae1dSRodney W. Grimes 	register struct ipasfrag *p, *prev;
611df8bae1dSRodney W. Grimes {
612df8bae1dSRodney W. Grimes 
613df8bae1dSRodney W. Grimes 	p->ipf_prev = prev;
614df8bae1dSRodney W. Grimes 	p->ipf_next = prev->ipf_next;
615df8bae1dSRodney W. Grimes 	prev->ipf_next->ipf_prev = p;
616df8bae1dSRodney W. Grimes 	prev->ipf_next = p;
617df8bae1dSRodney W. Grimes }
618df8bae1dSRodney W. Grimes 
619df8bae1dSRodney W. Grimes /*
620df8bae1dSRodney W. Grimes  * To ip_enq as remque is to insque.
621df8bae1dSRodney W. Grimes  */
622df8bae1dSRodney W. Grimes void
623df8bae1dSRodney W. Grimes ip_deq(p)
624df8bae1dSRodney W. Grimes 	register struct ipasfrag *p;
625df8bae1dSRodney W. Grimes {
626df8bae1dSRodney W. Grimes 
627df8bae1dSRodney W. Grimes 	p->ipf_prev->ipf_next = p->ipf_next;
628df8bae1dSRodney W. Grimes 	p->ipf_next->ipf_prev = p->ipf_prev;
629df8bae1dSRodney W. Grimes }
630df8bae1dSRodney W. Grimes 
631df8bae1dSRodney W. Grimes /*
632df8bae1dSRodney W. Grimes  * IP timer processing;
633df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
634df8bae1dSRodney W. Grimes  * queue, discard it.
635df8bae1dSRodney W. Grimes  */
636df8bae1dSRodney W. Grimes void
637df8bae1dSRodney W. Grimes ip_slowtimo()
638df8bae1dSRodney W. Grimes {
639df8bae1dSRodney W. Grimes 	register struct ipq *fp;
640df8bae1dSRodney W. Grimes 	int s = splnet();
641df8bae1dSRodney W. Grimes 
642df8bae1dSRodney W. Grimes 	fp = ipq.next;
643df8bae1dSRodney W. Grimes 	if (fp == 0) {
644df8bae1dSRodney W. Grimes 		splx(s);
645df8bae1dSRodney W. Grimes 		return;
646df8bae1dSRodney W. Grimes 	}
647df8bae1dSRodney W. Grimes 	while (fp != &ipq) {
648df8bae1dSRodney W. Grimes 		--fp->ipq_ttl;
649df8bae1dSRodney W. Grimes 		fp = fp->next;
650df8bae1dSRodney W. Grimes 		if (fp->prev->ipq_ttl == 0) {
651df8bae1dSRodney W. Grimes 			ipstat.ips_fragtimeout++;
652df8bae1dSRodney W. Grimes 			ip_freef(fp->prev);
653df8bae1dSRodney W. Grimes 		}
654df8bae1dSRodney W. Grimes 	}
655df8bae1dSRodney W. Grimes 	splx(s);
656df8bae1dSRodney W. Grimes }
657df8bae1dSRodney W. Grimes 
658df8bae1dSRodney W. Grimes /*
659df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
660df8bae1dSRodney W. Grimes  */
661df8bae1dSRodney W. Grimes void
662df8bae1dSRodney W. Grimes ip_drain()
663df8bae1dSRodney W. Grimes {
664df8bae1dSRodney W. Grimes 
665df8bae1dSRodney W. Grimes 	while (ipq.next != &ipq) {
666df8bae1dSRodney W. Grimes 		ipstat.ips_fragdropped++;
667df8bae1dSRodney W. Grimes 		ip_freef(ipq.next);
668df8bae1dSRodney W. Grimes 	}
669df8bae1dSRodney W. Grimes }
670df8bae1dSRodney W. Grimes 
671df8bae1dSRodney W. Grimes /*
672df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
673df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
674df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
675df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
676df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
677df8bae1dSRodney W. Grimes  */
678df8bae1dSRodney W. Grimes int
679df8bae1dSRodney W. Grimes ip_dooptions(m)
680df8bae1dSRodney W. Grimes 	struct mbuf *m;
681df8bae1dSRodney W. Grimes {
682df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
683df8bae1dSRodney W. Grimes 	register u_char *cp;
684df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
685df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
686df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
687df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
688df8bae1dSRodney W. Grimes 	n_time ntime;
689df8bae1dSRodney W. Grimes 
690df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
691df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
692df8bae1dSRodney W. Grimes 	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
693df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
694df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
695df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
696df8bae1dSRodney W. Grimes 			break;
697df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
698df8bae1dSRodney W. Grimes 			optlen = 1;
699df8bae1dSRodney W. Grimes 		else {
700df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
701df8bae1dSRodney W. Grimes 			if (optlen <= 0 || optlen > cnt) {
702df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
703df8bae1dSRodney W. Grimes 				goto bad;
704df8bae1dSRodney W. Grimes 			}
705df8bae1dSRodney W. Grimes 		}
706df8bae1dSRodney W. Grimes 		switch (opt) {
707df8bae1dSRodney W. Grimes 
708df8bae1dSRodney W. Grimes 		default:
709df8bae1dSRodney W. Grimes 			break;
710df8bae1dSRodney W. Grimes 
711df8bae1dSRodney W. Grimes 		/*
712df8bae1dSRodney W. Grimes 		 * Source routing with record.
713df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
714df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
715df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
716df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
717df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
718df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
719df8bae1dSRodney W. Grimes 		 */
720df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
721df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
722df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
723df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
724df8bae1dSRodney W. Grimes 				goto bad;
725df8bae1dSRodney W. Grimes 			}
726df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
727df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
728df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
729df8bae1dSRodney W. Grimes 			if (ia == 0) {
730df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
731df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
732df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
733df8bae1dSRodney W. Grimes 					goto bad;
734df8bae1dSRodney W. Grimes 				}
735df8bae1dSRodney W. Grimes 				/*
736df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
737df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
738df8bae1dSRodney W. Grimes 				 */
739df8bae1dSRodney W. Grimes 				break;
740df8bae1dSRodney W. Grimes 			}
741df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
742df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr)) {
743df8bae1dSRodney W. Grimes 				/*
744df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
745df8bae1dSRodney W. Grimes 				 */
746df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
747df8bae1dSRodney W. Grimes 				break;
748df8bae1dSRodney W. Grimes 			}
749df8bae1dSRodney W. Grimes 			/*
750df8bae1dSRodney W. Grimes 			 * locate outgoing interface
751df8bae1dSRodney W. Grimes 			 */
752df8bae1dSRodney W. Grimes 			bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
753df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
754df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
755df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
756df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
757df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
758df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
759df8bae1dSRodney W. Grimes 			} else
760df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
761df8bae1dSRodney W. Grimes 			if (ia == 0) {
762df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
763df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
764df8bae1dSRodney W. Grimes 				goto bad;
765df8bae1dSRodney W. Grimes 			}
766df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
767df8bae1dSRodney W. Grimes 			bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
768df8bae1dSRodney W. Grimes 			    (caddr_t)(cp + off), sizeof(struct in_addr));
769df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
770df8bae1dSRodney W. Grimes 			/*
771df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
772df8bae1dSRodney W. Grimes 			 */
773df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
774df8bae1dSRodney W. Grimes 			break;
775df8bae1dSRodney W. Grimes 
776df8bae1dSRodney W. Grimes 		case IPOPT_RR:
777df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
778df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
779df8bae1dSRodney W. Grimes 				goto bad;
780df8bae1dSRodney W. Grimes 			}
781df8bae1dSRodney W. Grimes 			/*
782df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
783df8bae1dSRodney W. Grimes 			 */
784df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
785df8bae1dSRodney W. Grimes 			if (off > optlen - sizeof(struct in_addr))
786df8bae1dSRodney W. Grimes 				break;
787df8bae1dSRodney W. Grimes 			bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
788df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
789df8bae1dSRodney W. Grimes 			/*
790df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
791df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
792df8bae1dSRodney W. Grimes 			 */
793df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
794df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
795df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
796df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
797df8bae1dSRodney W. Grimes 				goto bad;
798df8bae1dSRodney W. Grimes 			}
799df8bae1dSRodney W. Grimes 			bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
800df8bae1dSRodney W. Grimes 			    (caddr_t)(cp + off), sizeof(struct in_addr));
801df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
802df8bae1dSRodney W. Grimes 			break;
803df8bae1dSRodney W. Grimes 
804df8bae1dSRodney W. Grimes 		case IPOPT_TS:
805df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
806df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
807df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
808df8bae1dSRodney W. Grimes 				goto bad;
809df8bae1dSRodney W. Grimes 			if (ipt->ipt_ptr > ipt->ipt_len - sizeof (long)) {
810df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
811df8bae1dSRodney W. Grimes 					goto bad;
812df8bae1dSRodney W. Grimes 				break;
813df8bae1dSRodney W. Grimes 			}
814df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
815df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
816df8bae1dSRodney W. Grimes 
817df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
818df8bae1dSRodney W. Grimes 				break;
819df8bae1dSRodney W. Grimes 
820df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
821df8bae1dSRodney W. Grimes 				if (ipt->ipt_ptr + sizeof(n_time) +
822df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
823df8bae1dSRodney W. Grimes 					goto bad;
824df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
825df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
826df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
827df8bae1dSRodney W. Grimes 				if (ia == 0)
828df8bae1dSRodney W. Grimes 					continue;
829df8bae1dSRodney W. Grimes 				bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
830df8bae1dSRodney W. Grimes 				    (caddr_t)sin, sizeof(struct in_addr));
831df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
832df8bae1dSRodney W. Grimes 				break;
833df8bae1dSRodney W. Grimes 
834df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
835df8bae1dSRodney W. Grimes 				if (ipt->ipt_ptr + sizeof(n_time) +
836df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
837df8bae1dSRodney W. Grimes 					goto bad;
838df8bae1dSRodney W. Grimes 				bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
839df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
840df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
841df8bae1dSRodney W. Grimes 					continue;
842df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
843df8bae1dSRodney W. Grimes 				break;
844df8bae1dSRodney W. Grimes 
845df8bae1dSRodney W. Grimes 			default:
846df8bae1dSRodney W. Grimes 				goto bad;
847df8bae1dSRodney W. Grimes 			}
848df8bae1dSRodney W. Grimes 			ntime = iptime();
849df8bae1dSRodney W. Grimes 			bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
850df8bae1dSRodney W. Grimes 			    sizeof(n_time));
851df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
852df8bae1dSRodney W. Grimes 		}
853df8bae1dSRodney W. Grimes 	}
854df8bae1dSRodney W. Grimes 	if (forward) {
855df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
856df8bae1dSRodney W. Grimes 		return (1);
857df8bae1dSRodney W. Grimes 	}
858df8bae1dSRodney W. Grimes 	return (0);
859df8bae1dSRodney W. Grimes bad:
860df8bae1dSRodney W. Grimes 	ip->ip_len -= ip->ip_hl << 2;   /* XXX icmp_error adds in hdr length */
861df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
862df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
863df8bae1dSRodney W. Grimes 	return (1);
864df8bae1dSRodney W. Grimes }
865df8bae1dSRodney W. Grimes 
866df8bae1dSRodney W. Grimes /*
867df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
868df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
869df8bae1dSRodney W. Grimes  */
870df8bae1dSRodney W. Grimes struct in_ifaddr *
871df8bae1dSRodney W. Grimes ip_rtaddr(dst)
872df8bae1dSRodney W. Grimes 	 struct in_addr dst;
873df8bae1dSRodney W. Grimes {
874df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
875df8bae1dSRodney W. Grimes 
876df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
877df8bae1dSRodney W. Grimes 
878df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
879df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
880df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
881df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
882df8bae1dSRodney W. Grimes 		}
883df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
884df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
885df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
886df8bae1dSRodney W. Grimes 
8872c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
888df8bae1dSRodney W. Grimes 	}
889df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
890df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
891df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
892df8bae1dSRodney W. Grimes }
893df8bae1dSRodney W. Grimes 
894df8bae1dSRodney W. Grimes /*
895df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
896df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
897df8bae1dSRodney W. Grimes  */
898df8bae1dSRodney W. Grimes void
899df8bae1dSRodney W. Grimes save_rte(option, dst)
900df8bae1dSRodney W. Grimes 	u_char *option;
901df8bae1dSRodney W. Grimes 	struct in_addr dst;
902df8bae1dSRodney W. Grimes {
903df8bae1dSRodney W. Grimes 	unsigned olen;
904df8bae1dSRodney W. Grimes 
905df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
906df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
907df8bae1dSRodney W. Grimes 	if (ipprintfs)
908df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
909df8bae1dSRodney W. Grimes #endif
910df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
911df8bae1dSRodney W. Grimes 		return;
912df8bae1dSRodney W. Grimes 	bcopy((caddr_t)option, (caddr_t)ip_srcrt.srcopt, olen);
913df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
914df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
915df8bae1dSRodney W. Grimes }
916df8bae1dSRodney W. Grimes 
917df8bae1dSRodney W. Grimes /*
918df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
919df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
920df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
921df8bae1dSRodney W. Grimes  */
922df8bae1dSRodney W. Grimes struct mbuf *
923df8bae1dSRodney W. Grimes ip_srcroute()
924df8bae1dSRodney W. Grimes {
925df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
926df8bae1dSRodney W. Grimes 	register struct mbuf *m;
927df8bae1dSRodney W. Grimes 
928df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
929df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
930df8bae1dSRodney W. Grimes 	m = m_get(M_DONTWAIT, MT_SOOPTS);
931df8bae1dSRodney W. Grimes 	if (m == 0)
932df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
933df8bae1dSRodney W. Grimes 
934df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
935df8bae1dSRodney W. Grimes 
936df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
937df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
938df8bae1dSRodney W. Grimes 	    OPTSIZ;
939df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
940df8bae1dSRodney W. Grimes 	if (ipprintfs)
941df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
942df8bae1dSRodney W. Grimes #endif
943df8bae1dSRodney W. Grimes 
944df8bae1dSRodney W. Grimes 	/*
945df8bae1dSRodney W. Grimes 	 * First save first hop for return route
946df8bae1dSRodney W. Grimes 	 */
947df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
948df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
949df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
950df8bae1dSRodney W. Grimes 	if (ipprintfs)
951df8bae1dSRodney W. Grimes 		printf(" hops %lx", ntohl(mtod(m, struct in_addr *)->s_addr));
952df8bae1dSRodney W. Grimes #endif
953df8bae1dSRodney W. Grimes 
954df8bae1dSRodney W. Grimes 	/*
955df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
956df8bae1dSRodney W. Grimes 	 */
957df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
958df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
959df8bae1dSRodney W. Grimes 	bcopy((caddr_t)&ip_srcrt.nop,
960df8bae1dSRodney W. Grimes 	    mtod(m, caddr_t) + sizeof(struct in_addr), OPTSIZ);
961df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
962df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
963df8bae1dSRodney W. Grimes #undef OPTSIZ
964df8bae1dSRodney W. Grimes 	/*
965df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
966df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
967df8bae1dSRodney W. Grimes 	 */
968df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
969df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
970df8bae1dSRodney W. Grimes 		if (ipprintfs)
971df8bae1dSRodney W. Grimes 			printf(" %lx", ntohl(q->s_addr));
972df8bae1dSRodney W. Grimes #endif
973df8bae1dSRodney W. Grimes 		*q++ = *p--;
974df8bae1dSRodney W. Grimes 	}
975df8bae1dSRodney W. Grimes 	/*
976df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
977df8bae1dSRodney W. Grimes 	 */
978df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
979df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
980df8bae1dSRodney W. Grimes 	if (ipprintfs)
981df8bae1dSRodney W. Grimes 		printf(" %lx\n", ntohl(q->s_addr));
982df8bae1dSRodney W. Grimes #endif
983df8bae1dSRodney W. Grimes 	return (m);
984df8bae1dSRodney W. Grimes }
985df8bae1dSRodney W. Grimes 
986df8bae1dSRodney W. Grimes /*
987df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
988df8bae1dSRodney W. Grimes  * level protocol in the kernel.
989df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
990df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
991df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
992df8bae1dSRodney W. Grimes  */
993df8bae1dSRodney W. Grimes void
994df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
995df8bae1dSRodney W. Grimes 	register struct mbuf *m;
996df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
997df8bae1dSRodney W. Grimes {
998df8bae1dSRodney W. Grimes 	register int i;
999df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1000df8bae1dSRodney W. Grimes 	register caddr_t opts;
1001df8bae1dSRodney W. Grimes 	int olen;
1002df8bae1dSRodney W. Grimes 
1003df8bae1dSRodney W. Grimes 	olen = (ip->ip_hl<<2) - sizeof (struct ip);
1004df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1005df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1006df8bae1dSRodney W. Grimes 	bcopy(opts  + olen, opts, (unsigned)i);
1007df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1008df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1009df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
1010df8bae1dSRodney W. Grimes 	ip->ip_hl = sizeof(struct ip) >> 2;
1011df8bae1dSRodney W. Grimes }
1012df8bae1dSRodney W. Grimes 
1013df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1014df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1015df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1016df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1017df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1018df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1019df8bae1dSRodney W. Grimes 	ENOPROTOOPT
1020df8bae1dSRodney W. Grimes };
1021df8bae1dSRodney W. Grimes 
1022df8bae1dSRodney W. Grimes /*
1023df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1024df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1025df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1026df8bae1dSRodney W. Grimes  * of codes and types.
1027df8bae1dSRodney W. Grimes  *
1028df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1029df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1030df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1031df8bae1dSRodney W. Grimes  * protocol deal with that.
1032df8bae1dSRodney W. Grimes  *
1033df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1034df8bae1dSRodney W. Grimes  * via a source route.
1035df8bae1dSRodney W. Grimes  */
1036df8bae1dSRodney W. Grimes void
1037df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1038df8bae1dSRodney W. Grimes 	struct mbuf *m;
1039df8bae1dSRodney W. Grimes 	int srcrt;
1040df8bae1dSRodney W. Grimes {
1041df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1042df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1043df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
104426f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1045df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1046df8bae1dSRodney W. Grimes 	n_long dest;
1047df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
1048df8bae1dSRodney W. Grimes 
1049df8bae1dSRodney W. Grimes 	dest = 0;
1050df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1051df8bae1dSRodney W. Grimes 	if (ipprintfs)
105261ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1053623ae52eSPoul-Henning Kamp 			ip->ip_src.s_addr, ip->ip_dst.s_addr, ip->ip_ttl);
1054df8bae1dSRodney W. Grimes #endif
1055100ba1a6SJordan K. Hubbard 
1056100ba1a6SJordan K. Hubbard 
1057df8bae1dSRodney W. Grimes 	if (m->m_flags & M_BCAST || in_canforward(ip->ip_dst) == 0) {
1058df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1059df8bae1dSRodney W. Grimes 		m_freem(m);
1060df8bae1dSRodney W. Grimes 		return;
1061df8bae1dSRodney W. Grimes 	}
1062df8bae1dSRodney W. Grimes 	HTONS(ip->ip_id);
1063df8bae1dSRodney W. Grimes 	if (ip->ip_ttl <= IPTTLDEC) {
1064df8bae1dSRodney W. Grimes 		icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0);
1065df8bae1dSRodney W. Grimes 		return;
1066df8bae1dSRodney W. Grimes 	}
1067df8bae1dSRodney W. Grimes 	ip->ip_ttl -= IPTTLDEC;
1068df8bae1dSRodney W. Grimes 
1069df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1070df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1071df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1072df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1073df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1074df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1075df8bae1dSRodney W. Grimes 		}
1076df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1077df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1078df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1079df8bae1dSRodney W. Grimes 
10802c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1081df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1082df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1083df8bae1dSRodney W. Grimes 			return;
1084df8bae1dSRodney W. Grimes 		}
1085df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1086df8bae1dSRodney W. Grimes 	}
1087df8bae1dSRodney W. Grimes 
1088df8bae1dSRodney W. Grimes 	/*
1089df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1090df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1091df8bae1dSRodney W. Grimes 	 */
1092df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
1093df8bae1dSRodney W. Grimes 
10942c17fe93SGarrett Wollman #ifdef bogus
1095df8bae1dSRodney W. Grimes #ifdef GATEWAY
1096df8bae1dSRodney W. Grimes 	ip_ifmatrix[rt->rt_ifp->if_index +
1097df8bae1dSRodney W. Grimes 	     if_index * m->m_pkthdr.rcvif->if_index]++;
1098df8bae1dSRodney W. Grimes #endif
10992c17fe93SGarrett Wollman #endif
1100df8bae1dSRodney W. Grimes 	/*
1101df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1102df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1103df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1104df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1105df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1106df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1107df8bae1dSRodney W. Grimes 	 */
1108df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1109df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1110df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1111df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1112df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1113df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1114df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1115df8bae1dSRodney W. Grimes 
1116df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1117df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1118df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1119df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1120df8bae1dSRodney W. Grimes 		    else
1121df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1122df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1123df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1124df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1125df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1126df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1127df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1128df8bae1dSRodney W. Grimes #endif
1129df8bae1dSRodney W. Grimes 		}
1130df8bae1dSRodney W. Grimes 	}
1131df8bae1dSRodney W. Grimes 
1132df8bae1dSRodney W. Grimes 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING
1133df8bae1dSRodney W. Grimes #ifdef DIRECTED_BROADCAST
1134df8bae1dSRodney W. Grimes 			    | IP_ALLOWBROADCAST
1135df8bae1dSRodney W. Grimes #endif
1136df8bae1dSRodney W. Grimes 						, 0);
1137df8bae1dSRodney W. Grimes 	if (error)
1138df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1139df8bae1dSRodney W. Grimes 	else {
1140df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1141df8bae1dSRodney W. Grimes 		if (type)
1142df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1143df8bae1dSRodney W. Grimes 		else {
1144df8bae1dSRodney W. Grimes 			if (mcopy)
1145df8bae1dSRodney W. Grimes 				m_freem(mcopy);
1146df8bae1dSRodney W. Grimes 			return;
1147df8bae1dSRodney W. Grimes 		}
1148df8bae1dSRodney W. Grimes 	}
1149df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1150df8bae1dSRodney W. Grimes 		return;
1151df8bae1dSRodney W. Grimes 	destifp = NULL;
1152df8bae1dSRodney W. Grimes 
1153df8bae1dSRodney W. Grimes 	switch (error) {
1154df8bae1dSRodney W. Grimes 
1155df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1156df8bae1dSRodney W. Grimes 		/* type, code set above */
1157df8bae1dSRodney W. Grimes 		break;
1158df8bae1dSRodney W. Grimes 
1159df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1160df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1161df8bae1dSRodney W. Grimes 	case ENETDOWN:
1162df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1163df8bae1dSRodney W. Grimes 	default:
1164df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1165df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1166df8bae1dSRodney W. Grimes 		break;
1167df8bae1dSRodney W. Grimes 
1168df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1169df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1170df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
1171df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1172df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
1173df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1174df8bae1dSRodney W. Grimes 		break;
1175df8bae1dSRodney W. Grimes 
1176df8bae1dSRodney W. Grimes 	case ENOBUFS:
1177df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1178df8bae1dSRodney W. Grimes 		code = 0;
1179df8bae1dSRodney W. Grimes 		break;
1180df8bae1dSRodney W. Grimes 	}
1181df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1182df8bae1dSRodney W. Grimes }
1183df8bae1dSRodney W. Grimes 
1184df8bae1dSRodney W. Grimes int
1185df8bae1dSRodney W. Grimes ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
1186df8bae1dSRodney W. Grimes 	int *name;
1187df8bae1dSRodney W. Grimes 	u_int namelen;
1188df8bae1dSRodney W. Grimes 	void *oldp;
1189df8bae1dSRodney W. Grimes 	size_t *oldlenp;
1190df8bae1dSRodney W. Grimes 	void *newp;
1191df8bae1dSRodney W. Grimes 	size_t newlen;
1192df8bae1dSRodney W. Grimes {
1193df8bae1dSRodney W. Grimes 	/* All sysctl names at this level are terminal. */
1194df8bae1dSRodney W. Grimes 	if (namelen != 1)
1195df8bae1dSRodney W. Grimes 		return (ENOTDIR);
1196df8bae1dSRodney W. Grimes 
1197df8bae1dSRodney W. Grimes 	switch (name[0]) {
1198df8bae1dSRodney W. Grimes 	case IPCTL_FORWARDING:
1199df8bae1dSRodney W. Grimes 		return (sysctl_int(oldp, oldlenp, newp, newlen, &ipforwarding));
1200df8bae1dSRodney W. Grimes 	case IPCTL_SENDREDIRECTS:
1201df8bae1dSRodney W. Grimes 		return (sysctl_int(oldp, oldlenp, newp, newlen,
1202df8bae1dSRodney W. Grimes 			&ipsendredirects));
1203df8bae1dSRodney W. Grimes 	case IPCTL_DEFTTL:
1204df8bae1dSRodney W. Grimes 		return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_defttl));
1205df8bae1dSRodney W. Grimes #ifdef notyet
1206df8bae1dSRodney W. Grimes 	case IPCTL_DEFMTU:
1207df8bae1dSRodney W. Grimes 		return (sysctl_int(oldp, oldlenp, newp, newlen, &ip_mtu));
1208df8bae1dSRodney W. Grimes #endif
12095be2baf8SGarrett Wollman 	case IPCTL_RTEXPIRE:
1210ea80aed1SGarrett Wollman 		return (sysctl_int(oldp, oldlenp, newp, newlen,
1211ea80aed1SGarrett Wollman 				   &rtq_reallyold));
1212ea80aed1SGarrett Wollman 	case IPCTL_RTMINEXPIRE:
1213ea80aed1SGarrett Wollman 		return (sysctl_int(oldp, oldlenp, newp, newlen,
1214ea80aed1SGarrett Wollman 				   &rtq_minreallyold));
1215ea80aed1SGarrett Wollman 	case IPCTL_RTMAXCACHE:
1216ea80aed1SGarrett Wollman 		return (sysctl_int(oldp, oldlenp, newp, newlen,
1217ea80aed1SGarrett Wollman 				   &rtq_toomany));
1218df8bae1dSRodney W. Grimes 	default:
1219df8bae1dSRodney W. Grimes 		return (EOPNOTSUPP);
1220df8bae1dSRodney W. Grimes 	}
1221df8bae1dSRodney W. Grimes 	/* NOTREACHED */
1222df8bae1dSRodney W. Grimes }
1223f0068c4aSGarrett Wollman 
1224f0068c4aSGarrett Wollman int
1225f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1226f0068c4aSGarrett Wollman {
1227f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1228f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1229f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1230f0068c4aSGarrett Wollman 
1231f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1232f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1233f0068c4aSGarrett Wollman 
1234f0068c4aSGarrett Wollman 	ip_rsvpd = so;
1235f0068c4aSGarrett Wollman 
1236f0068c4aSGarrett Wollman 	return 0;
1237f0068c4aSGarrett Wollman }
1238f0068c4aSGarrett Wollman 
1239f0068c4aSGarrett Wollman int
1240f0068c4aSGarrett Wollman ip_rsvp_done(void)
1241f0068c4aSGarrett Wollman {
1242f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
1243f0068c4aSGarrett Wollman 	return 0;
1244f0068c4aSGarrett Wollman }
1245