xref: /freebsd/sys/netinet/ip_input.c (revision bfef7ed45cbdee30b500a702fd7b02c530d8c7df)
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
34c3aac50fSPeter Wemm  * $FreeBSD$
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
3758938916SGarrett Wollman #define	_IP_VHL
3858938916SGarrett Wollman 
39e4f4247aSEivind Eklund #include "opt_bootp.h"
4074a9466cSGary Palmer #include "opt_ipfw.h"
41b715f178SLuigi Rizzo #include "opt_ipdn.h"
42fbd1372aSJoerg Wunsch #include "opt_ipdivert.h"
431ee25934SPeter Wemm #include "opt_ipfilter.h"
4427108a15SDag-Erling Smørgrav #include "opt_ipstealth.h"
456a800098SYoshinobu Inoue #include "opt_ipsec.h"
46c4ac87eaSDarren Reed #include "opt_pfil_hooks.h"
4774a9466cSGary Palmer 
48df8bae1dSRodney W. Grimes #include <sys/param.h>
49df8bae1dSRodney W. Grimes #include <sys/systm.h>
50df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
51b715f178SLuigi Rizzo #include <sys/malloc.h>
52df8bae1dSRodney W. Grimes #include <sys/domain.h>
53df8bae1dSRodney W. Grimes #include <sys/protosw.h>
54df8bae1dSRodney W. Grimes #include <sys/socket.h>
55df8bae1dSRodney W. Grimes #include <sys/time.h>
56df8bae1dSRodney W. Grimes #include <sys/kernel.h>
571025071fSGarrett Wollman #include <sys/syslog.h>
58b5e8ce9fSBruce Evans #include <sys/sysctl.h>
59df8bae1dSRodney W. Grimes 
60c85540ddSAndrey A. Chernov #include <net/pfil.h>
61df8bae1dSRodney W. Grimes #include <net/if.h>
62d314ad7bSJulian Elischer #include <net/if_var.h>
6382c23ebaSBill Fenner #include <net/if_dl.h>
64df8bae1dSRodney W. Grimes #include <net/route.h>
65748e0b0aSGarrett Wollman #include <net/netisr.h>
66367d34f8SBrian Somers #include <net/intrq.h>
67df8bae1dSRodney W. Grimes 
68df8bae1dSRodney W. Grimes #include <netinet/in.h>
69df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
70b5e8ce9fSBruce Evans #include <netinet/in_var.h>
71df8bae1dSRodney W. Grimes #include <netinet/ip.h>
72df8bae1dSRodney W. Grimes #include <netinet/in_pcb.h>
73df8bae1dSRodney W. Grimes #include <netinet/ip_var.h>
74df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
7558938916SGarrett Wollman #include <machine/in_cksum.h>
76df8bae1dSRodney W. Grimes 
776a800098SYoshinobu Inoue #include <netinet/ipprotosw.h>
786a800098SYoshinobu Inoue 
79f0068c4aSGarrett Wollman #include <sys/socketvar.h>
806ddbf1e2SGary Palmer 
816ddbf1e2SGary Palmer #include <netinet/ip_fw.h>
826ddbf1e2SGary Palmer 
836a800098SYoshinobu Inoue #ifdef IPSEC
846a800098SYoshinobu Inoue #include <netinet6/ipsec.h>
856a800098SYoshinobu Inoue #include <netkey/key.h>
866a800098SYoshinobu Inoue #endif
876a800098SYoshinobu Inoue 
886a800098SYoshinobu Inoue #include "faith.h"
896a800098SYoshinobu Inoue #if defined(NFAITH) && NFAITH > 0
906a800098SYoshinobu Inoue #include <net/if_types.h>
916a800098SYoshinobu Inoue #endif
926a800098SYoshinobu Inoue 
93b715f178SLuigi Rizzo #ifdef DUMMYNET
94b715f178SLuigi Rizzo #include <netinet/ip_dummynet.h>
95b715f178SLuigi Rizzo #endif
96b715f178SLuigi Rizzo 
971c5de19aSGarrett Wollman int rsvp_on = 0;
98f708ef1bSPoul-Henning Kamp static int ip_rsvp_on;
99f0068c4aSGarrett Wollman struct socket *ip_rsvpd;
100f0068c4aSGarrett Wollman 
1011f91d8c5SDavid Greenman int	ipforwarding = 0;
1020312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW,
1033d177f46SBill Fumerola     &ipforwarding, 0, "Enable IP forwarding between interfaces");
1040312fbe9SPoul-Henning Kamp 
105d4fb926cSGarrett Wollman static int	ipsendredirects = 1; /* XXX */
1060312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW,
1073d177f46SBill Fumerola     &ipsendredirects, 0, "Enable sending IP redirects");
1080312fbe9SPoul-Henning Kamp 
109df8bae1dSRodney W. Grimes int	ip_defttl = IPDEFTTL;
1100312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW,
1113d177f46SBill Fumerola     &ip_defttl, 0, "Maximum TTL on IP packets");
1120312fbe9SPoul-Henning Kamp 
1130312fbe9SPoul-Henning Kamp static int	ip_dosourceroute = 0;
1140312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW,
1153d177f46SBill Fumerola     &ip_dosourceroute, 0, "Enable forwarding source routed IP packets");
1164fce5804SGuido van Rooij 
1174fce5804SGuido van Rooij static int	ip_acceptsourceroute = 0;
1184fce5804SGuido van Rooij SYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute,
1193d177f46SBill Fumerola     CTLFLAG_RW, &ip_acceptsourceroute, 0,
1203d177f46SBill Fumerola     "Enable accepting source routed IP packets");
1216a800098SYoshinobu Inoue 
1226a800098SYoshinobu Inoue static int	ip_keepfaith = 0;
1236a800098SYoshinobu Inoue SYSCTL_INT(_net_inet_ip, IPCTL_KEEPFAITH, keepfaith, CTLFLAG_RW,
1246a800098SYoshinobu Inoue 	&ip_keepfaith,	0,
1256a800098SYoshinobu Inoue 	"Enable packet capture for FAITH IPv4->IPv6 translater daemon");
1266a800098SYoshinobu Inoue 
127823db0e9SDon Lewis /*
128823db0e9SDon Lewis  * XXX - Setting ip_checkinterface mostly implements the receive side of
129823db0e9SDon Lewis  * the Strong ES model described in RFC 1122, but since the routing table
130a8f12100SDon Lewis  * and transmit implementation do not implement the Strong ES model,
131823db0e9SDon Lewis  * setting this to 1 results in an odd hybrid.
1323f67c834SDon Lewis  *
133a8f12100SDon Lewis  * XXX - ip_checkinterface currently must be disabled if you use ipnat
134a8f12100SDon Lewis  * to translate the destination address to another local interface.
1353f67c834SDon Lewis  *
1363f67c834SDon Lewis  * XXX - ip_checkinterface must be disabled if you add IP aliases
1373f67c834SDon Lewis  * to the loopback interface instead of the interface where the
1383f67c834SDon Lewis  * packets for those addresses are received.
139823db0e9SDon Lewis  */
140b3e95d4eSJonathan Lemon static int	ip_checkinterface = 1;
141b3e95d4eSJonathan Lemon SYSCTL_INT(_net_inet_ip, OID_AUTO, check_interface, CTLFLAG_RW,
142b3e95d4eSJonathan Lemon     &ip_checkinterface, 0, "Verify packet arrives on correct interface");
143b3e95d4eSJonathan Lemon 
144df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1450312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
146df8bae1dSRodney W. Grimes #endif
147df8bae1dSRodney W. Grimes 
148df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
1496a800098SYoshinobu Inoue extern	struct ipprotosw inetsw[];
150df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
1510312fbe9SPoul-Henning Kamp static int	ipqmaxlen = IFQ_MAXLEN;
15259562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; /* first inet address */
153afed1375SDavid Greenman SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RW,
1543d177f46SBill Fumerola     &ipintrq.ifq_maxlen, 0, "Maximum size of the IP input queue");
1550312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1563d177f46SBill Fumerola     &ipintrq.ifq_drops, 0, "Number of packets dropped from the IP input queue");
157df8bae1dSRodney W. Grimes 
158f23b4c91SGarrett Wollman struct ipstat ipstat;
1596fce01c9SGarrett Wollman SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RD,
1603d177f46SBill Fumerola     &ipstat, ipstat, "IP statistics (struct ipstat, netinet/ip_var.h)");
161194a213eSAndrey A. Chernov 
162194a213eSAndrey A. Chernov /* Packet reassembly stuff */
163194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
164194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
165194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
166194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
167831a80b0SMatthew Dillon 	(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
168194a213eSAndrey A. Chernov 
169194a213eSAndrey A. Chernov static struct ipq ipq[IPREASS_NHASH];
170194a213eSAndrey A. Chernov static int    nipq = 0;         /* total # of reass queues */
171194a213eSAndrey A. Chernov static int    maxnipq;
172367d34f8SBrian Somers const  int    ipintrq_present = 1;
173f23b4c91SGarrett Wollman 
1740312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1750312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1763d177f46SBill Fumerola     &ip_mtu, 0, "Default MTU");
1770312fbe9SPoul-Henning Kamp #endif
1780312fbe9SPoul-Henning Kamp 
1791b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
1801b968362SDag-Erling Smørgrav static int	ipstealth = 0;
1811b968362SDag-Erling Smørgrav SYSCTL_INT(_net_inet_ip, OID_AUTO, stealth, CTLFLAG_RW,
1821b968362SDag-Erling Smørgrav     &ipstealth, 0, "");
1831b968362SDag-Erling Smørgrav #endif
1841b968362SDag-Erling Smørgrav 
185cfe8b629SGarrett Wollman 
18623bf9953SPoul-Henning Kamp /* Firewall hooks */
18723bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
18823bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
1899fcc0795SLuigi Rizzo int fw_enable = 1 ;
190e7319babSPoul-Henning Kamp 
191b715f178SLuigi Rizzo #ifdef DUMMYNET
192b715f178SLuigi Rizzo ip_dn_ctl_t *ip_dn_ctl_ptr;
193b715f178SLuigi Rizzo #endif
194b715f178SLuigi Rizzo 
195afed1b49SDarren Reed 
196e7319babSPoul-Henning Kamp /*
197df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
198df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
199df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
200df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
201df8bae1dSRodney W. Grimes  * to us.
202df8bae1dSRodney W. Grimes  */
2030312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
204df8bae1dSRodney W. Grimes static	struct ip_srcrt {
205df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
206df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
207df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
208df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
209df8bae1dSRodney W. Grimes } ip_srcrt;
210df8bae1dSRodney W. Grimes 
211f9e354dfSJulian Elischer struct sockaddr_in *ip_fw_fwd_addr;
212f9e354dfSJulian Elischer 
213df8bae1dSRodney W. Grimes static void	save_rte __P((u_char *, struct in_addr));
2140312fbe9SPoul-Henning Kamp static int	ip_dooptions __P((struct mbuf *));
2150312fbe9SPoul-Henning Kamp static void	ip_forward __P((struct mbuf *, int));
2160312fbe9SPoul-Henning Kamp static void	ip_freef __P((struct ipq *));
2178948e4baSArchie Cobbs #ifdef IPDIVERT
2186a800098SYoshinobu Inoue static struct	mbuf *ip_reass __P((struct mbuf *,
2198948e4baSArchie Cobbs 			struct ipq *, struct ipq *, u_int32_t *, u_int16_t *));
2208948e4baSArchie Cobbs #else
2216a800098SYoshinobu Inoue static struct	mbuf *ip_reass __P((struct mbuf *, struct ipq *, struct ipq *));
2228948e4baSArchie Cobbs #endif
2238948e4baSArchie Cobbs static struct	in_ifaddr *ip_rtaddr __P((struct in_addr));
2240312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
2258948e4baSArchie Cobbs 
226df8bae1dSRodney W. Grimes /*
227df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
228df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
229df8bae1dSRodney W. Grimes  */
230df8bae1dSRodney W. Grimes void
231df8bae1dSRodney W. Grimes ip_init()
232df8bae1dSRodney W. Grimes {
2336a800098SYoshinobu Inoue 	register struct ipprotosw *pr;
234df8bae1dSRodney W. Grimes 	register int i;
235df8bae1dSRodney W. Grimes 
23659562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
2376a800098SYoshinobu Inoue 	pr = (struct ipprotosw *)pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
238df8bae1dSRodney W. Grimes 	if (pr == 0)
239df8bae1dSRodney W. Grimes 		panic("ip_init");
240df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
241df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
2426a800098SYoshinobu Inoue 	for (pr = (struct ipprotosw *)inetdomain.dom_protosw;
2436a800098SYoshinobu Inoue 	    pr < (struct ipprotosw *)inetdomain.dom_protoswNPROTOSW; pr++)
244df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
245df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
246df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
247194a213eSAndrey A. Chernov 
248194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
249194a213eSAndrey A. Chernov 	    ipq[i].next = ipq[i].prev = &ipq[i];
250194a213eSAndrey A. Chernov 
251194a213eSAndrey A. Chernov 	maxnipq = nmbclusters/4;
252194a213eSAndrey A. Chernov 
253227ee8a1SPoul-Henning Kamp 	ip_id = time_second & 0xffff;
254df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
255df5e1987SJonathan Lemon 	mtx_init(&ipintrq.ifq_mtx, "ip_inq", MTX_DEF);
256242c5536SPeter Wemm 
257242c5536SPeter Wemm 	register_netisr(NETISR_IP, ipintr);
258df8bae1dSRodney W. Grimes }
259df8bae1dSRodney W. Grimes 
2600312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
261f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
262df8bae1dSRodney W. Grimes 
263df8bae1dSRodney W. Grimes /*
264df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
265df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
266df8bae1dSRodney W. Grimes  */
267c67b1d17SGarrett Wollman void
268c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
269df8bae1dSRodney W. Grimes {
27023bf9953SPoul-Henning Kamp 	struct ip *ip;
27123bf9953SPoul-Henning Kamp 	struct ipq *fp;
2725da9f8faSJosef Karthauser 	struct in_ifaddr *ia = NULL;
273823db0e9SDon Lewis 	int    i, hlen, checkif;
27447c861ecSBrian Somers 	u_short sum;
2758948e4baSArchie Cobbs 	u_int16_t divert_cookie;		/* firewall cookie */
2767538a9a0SJonathan Lemon 	struct in_addr pkt_dst;
2778948e4baSArchie Cobbs #ifdef IPDIVERT
2788948e4baSArchie Cobbs 	u_int32_t divert_info = 0;		/* packet divert/tee info */
279b715f178SLuigi Rizzo #endif
280b715f178SLuigi Rizzo 	struct ip_fw_chain *rule = NULL;
281c4ac87eaSDarren Reed #ifdef PFIL_HOOKS
282c4ac87eaSDarren Reed 	struct packet_filter_hook *pfh;
283c4ac87eaSDarren Reed 	struct mbuf *m0;
284c4ac87eaSDarren Reed 	int rv;
285c4ac87eaSDarren Reed #endif /* PFIL_HOOKS */
286b715f178SLuigi Rizzo 
2878948e4baSArchie Cobbs #ifdef IPDIVERT
2888948e4baSArchie Cobbs 	/* Get and reset firewall cookie */
2898948e4baSArchie Cobbs 	divert_cookie = ip_divert_cookie;
2908948e4baSArchie Cobbs 	ip_divert_cookie = 0;
2918948e4baSArchie Cobbs #else
2928948e4baSArchie Cobbs 	divert_cookie = 0;
2938948e4baSArchie Cobbs #endif
2948948e4baSArchie Cobbs 
295b715f178SLuigi Rizzo #if defined(IPFIREWALL) && defined(DUMMYNET)
296b715f178SLuigi Rizzo         /*
297b715f178SLuigi Rizzo          * dummynet packet are prepended a vestigial mbuf with
298b715f178SLuigi Rizzo          * m_type = MT_DUMMYNET and m_data pointing to the matching
299b715f178SLuigi Rizzo          * rule.
300b715f178SLuigi Rizzo          */
301b715f178SLuigi Rizzo         if (m->m_type == MT_DUMMYNET) {
302b715f178SLuigi Rizzo             rule = (struct ip_fw_chain *)(m->m_data) ;
303b715f178SLuigi Rizzo             m = m->m_next ;
304b715f178SLuigi Rizzo             ip = mtod(m, struct ip *);
305b715f178SLuigi Rizzo             hlen = IP_VHL_HL(ip->ip_vhl) << 2;
306b715f178SLuigi Rizzo             goto iphack ;
307b715f178SLuigi Rizzo         } else
308b715f178SLuigi Rizzo             rule = NULL ;
309b715f178SLuigi Rizzo #endif
310df8bae1dSRodney W. Grimes 
311df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
312ed7509acSJulian Elischer 	if (m == NULL || (m->m_flags & M_PKTHDR) == 0)
31358938916SGarrett Wollman 		panic("ip_input no HDR");
314df8bae1dSRodney W. Grimes #endif
315df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
31658938916SGarrett Wollman 
31758938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
31858938916SGarrett Wollman 		goto tooshort;
31958938916SGarrett Wollman 
320df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
321df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
322df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
323c67b1d17SGarrett Wollman 		return;
324df8bae1dSRodney W. Grimes 	}
325df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
32658938916SGarrett Wollman 
32758938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
328df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
329df8bae1dSRodney W. Grimes 		goto bad;
330df8bae1dSRodney W. Grimes 	}
33158938916SGarrett Wollman 
33258938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
333df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
334df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
335df8bae1dSRodney W. Grimes 		goto bad;
336df8bae1dSRodney W. Grimes 	}
337df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
338df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
339df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
340c67b1d17SGarrett Wollman 			return;
341df8bae1dSRodney W. Grimes 		}
342df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
343df8bae1dSRodney W. Grimes 	}
344db4f9cc7SJonathan Lemon 	if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) {
345db4f9cc7SJonathan Lemon 		sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
346db4f9cc7SJonathan Lemon 	} else {
34758938916SGarrett Wollman 		if (hlen == sizeof(struct ip)) {
34847c861ecSBrian Somers 			sum = in_cksum_hdr(ip);
34958938916SGarrett Wollman 		} else {
35047c861ecSBrian Somers 			sum = in_cksum(m, hlen);
35158938916SGarrett Wollman 		}
352db4f9cc7SJonathan Lemon 	}
35347c861ecSBrian Somers 	if (sum) {
354df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
355df8bae1dSRodney W. Grimes 		goto bad;
356df8bae1dSRodney W. Grimes 	}
357df8bae1dSRodney W. Grimes 
358df8bae1dSRodney W. Grimes 	/*
359df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
360df8bae1dSRodney W. Grimes 	 */
361df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
362df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
363df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
364df8bae1dSRodney W. Grimes 		goto bad;
365df8bae1dSRodney W. Grimes 	}
366df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
367df8bae1dSRodney W. Grimes 
368df8bae1dSRodney W. Grimes 	/*
369df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
370df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
371df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
372df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
373df8bae1dSRodney W. Grimes 	 */
374df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
37558938916SGarrett Wollman tooshort:
376df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
377df8bae1dSRodney W. Grimes 		goto bad;
378df8bae1dSRodney W. Grimes 	}
379df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
380df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
381df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
382df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
383df8bae1dSRodney W. Grimes 		} else
384df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
385df8bae1dSRodney W. Grimes 	}
3863f67c834SDon Lewis 
3873f67c834SDon Lewis 	/*
3883f67c834SDon Lewis 	 * Don't accept packets with a loopback destination address
3893f67c834SDon Lewis 	 * unless they arrived via the loopback interface.
3903f67c834SDon Lewis 	 */
3913f67c834SDon Lewis 	if ((ntohl(ip->ip_dst.s_addr) & IN_CLASSA_NET) ==
3923f67c834SDon Lewis 	    (IN_LOOPBACKNET << IN_CLASSA_NSHIFT) &&
3933f67c834SDon Lewis 	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
3943f67c834SDon Lewis 		goto bad;
3953f67c834SDon Lewis 	}
3963f67c834SDon Lewis 
3974dd1662bSUgen J.S. Antsilevich 	/*
3984dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3994dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
4004dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
4014dd1662bSUgen J.S. Antsilevich 	 * deals with it.
40293e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
403fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
404b715f178SLuigi Rizzo 	 * - Pipe: pass pkt through dummynet.
4054dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
4064dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
4074dd1662bSUgen J.S. Antsilevich  	 */
408b715f178SLuigi Rizzo 
409dee383e0SEivind Eklund #if defined(IPFIREWALL) && defined(DUMMYNET)
410b715f178SLuigi Rizzo iphack:
411dee383e0SEivind Eklund #endif
412df8bae1dSRodney W. Grimes 
413c4ac87eaSDarren Reed #ifdef PFIL_HOOKS
414c4ac87eaSDarren Reed 	/*
415c4ac87eaSDarren Reed 	 * Run through list of hooks for input packets.  If there are any
416c4ac87eaSDarren Reed 	 * filters which require that additional packets in the flow are
417c4ac87eaSDarren Reed 	 * not fast-forwarded, they must clear the M_CANFASTFWD flag.
418c4ac87eaSDarren Reed 	 * Note that filters must _never_ set this flag, as another filter
419c4ac87eaSDarren Reed 	 * in the list may have previously cleared it.
420c4ac87eaSDarren Reed 	 */
421c4ac87eaSDarren Reed 	m0 = m;
422c4ac87eaSDarren Reed 	pfh = pfil_hook_get(PFIL_IN, &inetsw[ip_protox[IPPROTO_IP]].pr_pfh);
423fc2ffbe6SPoul-Henning Kamp 	for (; pfh; pfh = TAILQ_NEXT(pfh, pfil_link))
424c4ac87eaSDarren Reed 		if (pfh->pfil_func) {
425c4ac87eaSDarren Reed 			rv = pfh->pfil_func(ip, hlen,
426c4ac87eaSDarren Reed 					    m->m_pkthdr.rcvif, 0, &m0);
427c4ac87eaSDarren Reed 			if (rv)
428beec8214SDarren Reed 				return;
429c4ac87eaSDarren Reed 			m = m0;
430c4ac87eaSDarren Reed 			if (m == NULL)
431c4ac87eaSDarren Reed 				return;
432c4ac87eaSDarren Reed 			ip = mtod(m, struct ip *);
433beec8214SDarren Reed 		}
434c4ac87eaSDarren Reed #endif /* PFIL_HOOKS */
435c4ac87eaSDarren Reed 
4366bc748b0SLuigi Rizzo 	if (fw_enable && ip_fw_chk_ptr) {
437f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
438f9e354dfSJulian Elischer 		/*
439f9e354dfSJulian Elischer 		 * If we've been forwarded from the output side, then
440f9e354dfSJulian Elischer 		 * skip the firewall a second time
441f9e354dfSJulian Elischer 		 */
442f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr)
443f9e354dfSJulian Elischer 			goto ours;
444f9e354dfSJulian Elischer #endif	/* IPFIREWALL_FORWARD */
445f9e354dfSJulian Elischer 		/*
4468948e4baSArchie Cobbs 		 * See the comment in ip_output for the return values
447b715f178SLuigi Rizzo 		 * produced by the firewall.
448f9e354dfSJulian Elischer 		 */
4498948e4baSArchie Cobbs 		i = (*ip_fw_chk_ptr)(&ip,
4508948e4baSArchie Cobbs 		    hlen, NULL, &divert_cookie, &m, &rule, &ip_fw_fwd_addr);
451507b4b54SLuigi Rizzo 		if (i & IP_FW_PORT_DENY_FLAG) { /* XXX new interface-denied */
452507b4b54SLuigi Rizzo 		    if (m)
453507b4b54SLuigi Rizzo 			m_freem(m);
454b715f178SLuigi Rizzo 		    return ;
455507b4b54SLuigi Rizzo 		}
456507b4b54SLuigi Rizzo 		if (m == NULL) {	/* Packet discarded by firewall */
457507b4b54SLuigi Rizzo 		    static int __debug=10;
458507b4b54SLuigi Rizzo 		    if (__debug >0) {
459507b4b54SLuigi Rizzo 			printf("firewall returns NULL, please update!\n");
460507b4b54SLuigi Rizzo 			__debug-- ;
461507b4b54SLuigi Rizzo 		    }
462507b4b54SLuigi Rizzo 		    return;
463507b4b54SLuigi Rizzo 		}
464b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr == NULL)	/* common case */
465b715f178SLuigi Rizzo 			goto pass;
466b715f178SLuigi Rizzo #ifdef DUMMYNET
4678948e4baSArchie Cobbs                 if ((i & IP_FW_PORT_DYNT_FLAG) != 0) {
4688948e4baSArchie Cobbs                         /* Send packet to the appropriate pipe */
4696a800098SYoshinobu Inoue                         dummynet_io(i&0xffff,DN_TO_IP_IN,m,NULL,NULL,0, rule,
4706a800098SYoshinobu Inoue 				    0);
471e4676ba6SJulian Elischer 			return;
47293e0e116SJulian Elischer 		}
473b715f178SLuigi Rizzo #endif
474b715f178SLuigi Rizzo #ifdef IPDIVERT
4758948e4baSArchie Cobbs 		if (i != 0 && (i & IP_FW_PORT_DYNT_FLAG) == 0) {
4768948e4baSArchie Cobbs 			/* Divert or tee packet */
4778948e4baSArchie Cobbs 			divert_info = i;
478b715f178SLuigi Rizzo 			goto ours;
479b715f178SLuigi Rizzo 		}
480b715f178SLuigi Rizzo #endif
481b715f178SLuigi Rizzo #ifdef IPFIREWALL_FORWARD
482b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr != NULL)
483b715f178SLuigi Rizzo 			goto pass;
484b715f178SLuigi Rizzo #endif
485b715f178SLuigi Rizzo 		/*
486b715f178SLuigi Rizzo 		 * if we get here, the packet must be dropped
487b715f178SLuigi Rizzo 		 */
488b715f178SLuigi Rizzo 		m_freem(m);
489b715f178SLuigi Rizzo 		return;
490b715f178SLuigi Rizzo 	}
491b715f178SLuigi Rizzo pass:
492100ba1a6SJordan K. Hubbard 
493df8bae1dSRodney W. Grimes 	/*
494df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
495df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
496df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
497df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
498df8bae1dSRodney W. Grimes 	 */
499df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
500ed1ff184SJulian Elischer 	if (hlen > sizeof (struct ip) && ip_dooptions(m)) {
501ed1ff184SJulian Elischer #ifdef IPFIREWALL_FORWARD
502ed1ff184SJulian Elischer 		ip_fw_fwd_addr = NULL;
503ed1ff184SJulian Elischer #endif
504c67b1d17SGarrett Wollman 		return;
505ed1ff184SJulian Elischer 	}
506df8bae1dSRodney W. Grimes 
507f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
508f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
509f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
510f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
511f0068c4aSGarrett Wollman 	 * grabbing the packet.
512f0068c4aSGarrett Wollman          */
5131c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
514f0068c4aSGarrett Wollman 		goto ours;
515f0068c4aSGarrett Wollman 
516df8bae1dSRodney W. Grimes 	/*
517df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
518cc766e04SGarrett Wollman 	 * If we don't have any addresses, assume any unicast packet
519cc766e04SGarrett Wollman 	 * we receive might be for us (and let the upper layers deal
520cc766e04SGarrett Wollman 	 * with it).
521df8bae1dSRodney W. Grimes 	 */
522cc766e04SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead) &&
523cc766e04SGarrett Wollman 	    (m->m_flags & (M_MCAST|M_BCAST)) == 0)
524cc766e04SGarrett Wollman 		goto ours;
525cc766e04SGarrett Wollman 
5267538a9a0SJonathan Lemon 	/*
5277538a9a0SJonathan Lemon 	 * Cache the destination address of the packet; this may be
5287538a9a0SJonathan Lemon 	 * changed by use of 'ipfw fwd'.
5297538a9a0SJonathan Lemon 	 */
5307538a9a0SJonathan Lemon 	pkt_dst = ip_fw_fwd_addr == NULL ?
5317538a9a0SJonathan Lemon 	    ip->ip_dst : ip_fw_fwd_addr->sin_addr;
5327538a9a0SJonathan Lemon 
533823db0e9SDon Lewis 	/*
534823db0e9SDon Lewis 	 * Enable a consistency check between the destination address
535823db0e9SDon Lewis 	 * and the arrival interface for a unicast packet (the RFC 1122
536823db0e9SDon Lewis 	 * strong ES model) if IP forwarding is disabled and the packet
537e15ae1b2SDon Lewis 	 * is not locally generated and the packet is not subject to
538e15ae1b2SDon Lewis 	 * 'ipfw fwd'.
5393f67c834SDon Lewis 	 *
5403f67c834SDon Lewis          * XXX - Checking also should be disabled if the destination
5413f67c834SDon Lewis 	 * address is ipnat'ed to a different interface.
5423f67c834SDon Lewis 	 *
543a8f12100SDon Lewis 	 * XXX - Checking is incompatible with IP aliases added
5443f67c834SDon Lewis 	 * to the loopback interface instead of the interface where
5453f67c834SDon Lewis 	 * the packets are received.
546823db0e9SDon Lewis 	 */
547823db0e9SDon Lewis 	checkif = ip_checkinterface && (ipforwarding == 0) &&
548e15ae1b2SDon Lewis 	    ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) &&
549e15ae1b2SDon Lewis 	    (ip_fw_fwd_addr == NULL);
550823db0e9SDon Lewis 
55137d40066SPoul-Henning Kamp 	TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
552df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
553df8bae1dSRodney W. Grimes 
554432aad0eSTor Egge #ifdef BOOTP_COMPAT
555432aad0eSTor Egge 		if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
556432aad0eSTor Egge 			goto ours;
557432aad0eSTor Egge #endif
558f9e354dfSJulian Elischer 		/*
559823db0e9SDon Lewis 		 * If the address matches, verify that the packet
560823db0e9SDon Lewis 		 * arrived via the correct interface if checking is
561823db0e9SDon Lewis 		 * enabled.
562f9e354dfSJulian Elischer 		 */
563823db0e9SDon Lewis 		if (IA_SIN(ia)->sin_addr.s_addr == pkt_dst.s_addr &&
564823db0e9SDon Lewis 		    (!checkif || ia->ia_ifp == m->m_pkthdr.rcvif))
565ed1ff184SJulian Elischer 			goto ours;
566823db0e9SDon Lewis 		/*
567823db0e9SDon Lewis 		 * Only accept broadcast packets that arrive via the
568823db0e9SDon Lewis 		 * matching interface.  Reception of forwarded directed
569823db0e9SDon Lewis 		 * broadcasts would be handled via ip_forward() and
570823db0e9SDon Lewis 		 * ether_output() with the loopback into the stack for
571823db0e9SDon Lewis 		 * SIMPLEX interfaces handled by ether_output().
572823db0e9SDon Lewis 		 */
573823db0e9SDon Lewis 		if (ia->ia_ifp == m->m_pkthdr.rcvif &&
574823db0e9SDon Lewis 		    ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
575df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
5767538a9a0SJonathan Lemon 			    pkt_dst.s_addr)
577df8bae1dSRodney W. Grimes 				goto ours;
5787538a9a0SJonathan Lemon 			if (ia->ia_netbroadcast.s_addr == pkt_dst.s_addr)
579df8bae1dSRodney W. Grimes 				goto ours;
580df8bae1dSRodney W. Grimes 		}
581df8bae1dSRodney W. Grimes 	}
582df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
583df8bae1dSRodney W. Grimes 		struct in_multi *inm;
584df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
585df8bae1dSRodney W. Grimes 			/*
586df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
587df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
588df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
589df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
590df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
591df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
592df8bae1dSRodney W. Grimes 			 */
593f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
594df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
595df8bae1dSRodney W. Grimes 				m_freem(m);
596c67b1d17SGarrett Wollman 				return;
597df8bae1dSRodney W. Grimes 			}
598df8bae1dSRodney W. Grimes 
599df8bae1dSRodney W. Grimes 			/*
600df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
601df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
602df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
603df8bae1dSRodney W. Grimes 			 */
604df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
605df8bae1dSRodney W. Grimes 				goto ours;
606df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
607df8bae1dSRodney W. Grimes 		}
608df8bae1dSRodney W. Grimes 		/*
609df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
610df8bae1dSRodney W. Grimes 		 * arrival interface.
611df8bae1dSRodney W. Grimes 		 */
612df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
613df8bae1dSRodney W. Grimes 		if (inm == NULL) {
61482c39223SGarrett Wollman 			ipstat.ips_notmember++;
615df8bae1dSRodney W. Grimes 			m_freem(m);
616c67b1d17SGarrett Wollman 			return;
617df8bae1dSRodney W. Grimes 		}
618df8bae1dSRodney W. Grimes 		goto ours;
619df8bae1dSRodney W. Grimes 	}
620df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
621df8bae1dSRodney W. Grimes 		goto ours;
622df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
623df8bae1dSRodney W. Grimes 		goto ours;
624df8bae1dSRodney W. Grimes 
6256a800098SYoshinobu Inoue #if defined(NFAITH) && 0 < NFAITH
6266a800098SYoshinobu Inoue 	/*
6276a800098SYoshinobu Inoue 	 * FAITH(Firewall Aided Internet Translator)
6286a800098SYoshinobu Inoue 	 */
6296a800098SYoshinobu Inoue 	if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
6306a800098SYoshinobu Inoue 		if (ip_keepfaith) {
6316a800098SYoshinobu Inoue 			if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_ICMP)
6326a800098SYoshinobu Inoue 				goto ours;
6336a800098SYoshinobu Inoue 		}
6346a800098SYoshinobu Inoue 		m_freem(m);
6356a800098SYoshinobu Inoue 		return;
6366a800098SYoshinobu Inoue 	}
6376a800098SYoshinobu Inoue #endif
638df8bae1dSRodney W. Grimes 	/*
639df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
640df8bae1dSRodney W. Grimes 	 */
641df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
642df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
643df8bae1dSRodney W. Grimes 		m_freem(m);
644df8bae1dSRodney W. Grimes 	} else
645df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
646ed1ff184SJulian Elischer #ifdef IPFIREWALL_FORWARD
647ed1ff184SJulian Elischer 	ip_fw_fwd_addr = NULL;
648ed1ff184SJulian Elischer #endif
649c67b1d17SGarrett Wollman 	return;
650df8bae1dSRodney W. Grimes 
651df8bae1dSRodney W. Grimes ours:
6525da9f8faSJosef Karthauser 	/* Count the packet in the ip address stats */
6535da9f8faSJosef Karthauser 	if (ia != NULL) {
6545da9f8faSJosef Karthauser 		ia->ia_ifa.if_ipackets++;
6555da9f8faSJosef Karthauser 		ia->ia_ifa.if_ibytes += m->m_pkthdr.len;
6565da9f8faSJosef Karthauser 	}
657100ba1a6SJordan K. Hubbard 
65863f8d699SJordan K. Hubbard 	/*
659df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
660df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
661df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
662df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
663df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
664df8bae1dSRodney W. Grimes 	 */
665b6ea1aa5SRuslan Ermilov 	if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
6666a800098SYoshinobu Inoue 
667194a213eSAndrey A. Chernov 		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
668df8bae1dSRodney W. Grimes 		/*
669df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
670df8bae1dSRodney W. Grimes 		 * of this datagram.
671df8bae1dSRodney W. Grimes 		 */
672194a213eSAndrey A. Chernov 		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
673df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
674df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
675df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
676df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
677df8bae1dSRodney W. Grimes 				goto found;
678df8bae1dSRodney W. Grimes 
679194a213eSAndrey A. Chernov 		fp = 0;
680194a213eSAndrey A. Chernov 
681194a213eSAndrey A. Chernov 		/* check if there's a place for the new queue */
682194a213eSAndrey A. Chernov 		if (nipq > maxnipq) {
683194a213eSAndrey A. Chernov 		    /*
684194a213eSAndrey A. Chernov 		     * drop something from the tail of the current queue
685194a213eSAndrey A. Chernov 		     * before proceeding further
686194a213eSAndrey A. Chernov 		     */
687194a213eSAndrey A. Chernov 		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */
688194a213eSAndrey A. Chernov 			for (i = 0; i < IPREASS_NHASH; i++) {
689194a213eSAndrey A. Chernov 			    if (ipq[i].prev != &ipq[i]) {
690194a213eSAndrey A. Chernov 				ip_freef(ipq[i].prev);
691194a213eSAndrey A. Chernov 				break;
692194a213eSAndrey A. Chernov 			    }
693194a213eSAndrey A. Chernov 			}
694194a213eSAndrey A. Chernov 		    } else
695194a213eSAndrey A. Chernov 			ip_freef(ipq[sum].prev);
696194a213eSAndrey A. Chernov 		}
697194a213eSAndrey A. Chernov found:
698df8bae1dSRodney W. Grimes 		/*
699df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
700df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
701df8bae1dSRodney W. Grimes 		 */
702df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
703b6ea1aa5SRuslan Ermilov 		if (ip->ip_off & IP_MF) {
7046effc713SDoug Rabson 		        /*
7056effc713SDoug Rabson 		         * Make sure that fragments have a data length
7066effc713SDoug Rabson 			 * that's a non-zero multiple of 8 bytes.
7076effc713SDoug Rabson 		         */
7086effc713SDoug Rabson 			if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
7096effc713SDoug Rabson 				ipstat.ips_toosmall++; /* XXX */
7106effc713SDoug Rabson 				goto bad;
7116effc713SDoug Rabson 			}
7126effc713SDoug Rabson 			m->m_flags |= M_FRAG;
7136effc713SDoug Rabson 		}
714df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
715df8bae1dSRodney W. Grimes 
716df8bae1dSRodney W. Grimes 		/*
717b6ea1aa5SRuslan Ermilov 		 * Attempt reassembly; if it succeeds, proceed.
718df8bae1dSRodney W. Grimes 		 */
719df8bae1dSRodney W. Grimes 		ipstat.ips_fragments++;
720487bdb38SRuslan Ermilov 		m->m_pkthdr.header = ip;
7218948e4baSArchie Cobbs #ifdef IPDIVERT
7226a800098SYoshinobu Inoue 		m = ip_reass(m,
7238948e4baSArchie Cobbs 		    fp, &ipq[sum], &divert_info, &divert_cookie);
7248948e4baSArchie Cobbs #else
7256a800098SYoshinobu Inoue 		m = ip_reass(m, fp, &ipq[sum]);
7268948e4baSArchie Cobbs #endif
7276a800098SYoshinobu Inoue 		if (m == 0) {
728f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
729f9e354dfSJulian Elischer 			ip_fw_fwd_addr = NULL;
730f9e354dfSJulian Elischer #endif
731c67b1d17SGarrett Wollman 			return;
732f9e354dfSJulian Elischer 		}
733df8bae1dSRodney W. Grimes 		ipstat.ips_reassembled++;
7346a800098SYoshinobu Inoue 		ip = mtod(m, struct ip *);
7357e2df452SRuslan Ermilov 		/* Get the header length of the reassembled packet */
7367e2df452SRuslan Ermilov 		hlen = IP_VHL_HL(ip->ip_vhl) << 2;
737af782f1cSBrian Somers #ifdef IPDIVERT
7388948e4baSArchie Cobbs 		/* Restore original checksum before diverting packet */
7398948e4baSArchie Cobbs 		if (divert_info != 0) {
740af782f1cSBrian Somers 			ip->ip_len += hlen;
741af782f1cSBrian Somers 			HTONS(ip->ip_len);
742af782f1cSBrian Somers 			HTONS(ip->ip_off);
743af782f1cSBrian Somers 			ip->ip_sum = 0;
74460123168SRuslan Ermilov 			if (hlen == sizeof(struct ip))
745af782f1cSBrian Somers 				ip->ip_sum = in_cksum_hdr(ip);
74660123168SRuslan Ermilov 			else
74760123168SRuslan Ermilov 				ip->ip_sum = in_cksum(m, hlen);
748af782f1cSBrian Somers 			NTOHS(ip->ip_off);
749af782f1cSBrian Somers 			NTOHS(ip->ip_len);
750af782f1cSBrian Somers 			ip->ip_len -= hlen;
751af782f1cSBrian Somers 		}
752af782f1cSBrian Somers #endif
753df8bae1dSRodney W. Grimes 	} else
754df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
755df8bae1dSRodney W. Grimes 
75693e0e116SJulian Elischer #ifdef IPDIVERT
75793e0e116SJulian Elischer 	/*
7588948e4baSArchie Cobbs 	 * Divert or tee packet to the divert protocol if required.
7598948e4baSArchie Cobbs 	 *
7608948e4baSArchie Cobbs 	 * If divert_info is zero then cookie should be too, so we shouldn't
7618948e4baSArchie Cobbs 	 * need to clear them here.  Assume divert_packet() does so also.
76293e0e116SJulian Elischer 	 */
7638948e4baSArchie Cobbs 	if (divert_info != 0) {
7648948e4baSArchie Cobbs 		struct mbuf *clone = NULL;
7658948e4baSArchie Cobbs 
7668948e4baSArchie Cobbs 		/* Clone packet if we're doing a 'tee' */
7678948e4baSArchie Cobbs 		if ((divert_info & IP_FW_PORT_TEE_FLAG) != 0)
7688948e4baSArchie Cobbs 			clone = m_dup(m, M_DONTWAIT);
7698948e4baSArchie Cobbs 
7708948e4baSArchie Cobbs 		/* Restore packet header fields to original values */
7718948e4baSArchie Cobbs 		ip->ip_len += hlen;
7728948e4baSArchie Cobbs 		HTONS(ip->ip_len);
7738948e4baSArchie Cobbs 		HTONS(ip->ip_off);
7748948e4baSArchie Cobbs 
7758948e4baSArchie Cobbs 		/* Deliver packet to divert input routine */
7768948e4baSArchie Cobbs 		ip_divert_cookie = divert_cookie;
7778948e4baSArchie Cobbs 		divert_packet(m, 1, divert_info & 0xffff);
778e4676ba6SJulian Elischer 		ipstat.ips_delivered++;
7798948e4baSArchie Cobbs 
7808948e4baSArchie Cobbs 		/* If 'tee', continue with original packet */
7818948e4baSArchie Cobbs 		if (clone == NULL)
78293e0e116SJulian Elischer 			return;
7838948e4baSArchie Cobbs 		m = clone;
7848948e4baSArchie Cobbs 		ip = mtod(m, struct ip *);
78593e0e116SJulian Elischer 	}
78693e0e116SJulian Elischer #endif
78793e0e116SJulian Elischer 
788df8bae1dSRodney W. Grimes 	/*
789df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
790df8bae1dSRodney W. Grimes 	 */
791df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
7926a800098SYoshinobu Inoue     {
7936a800098SYoshinobu Inoue 	int off = hlen, nh = ip->ip_p;
7946a800098SYoshinobu Inoue 
7956a800098SYoshinobu Inoue 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, off, nh);
796f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
797f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;	/* tcp needed it */
798f9e354dfSJulian Elischer #endif
799c67b1d17SGarrett Wollman 	return;
8006a800098SYoshinobu Inoue     }
801df8bae1dSRodney W. Grimes bad:
802f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
803f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;
804f9e354dfSJulian Elischer #endif
805df8bae1dSRodney W. Grimes 	m_freem(m);
806c67b1d17SGarrett Wollman }
807c67b1d17SGarrett Wollman 
808c67b1d17SGarrett Wollman /*
809c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
810c67b1d17SGarrett Wollman  */
811c67b1d17SGarrett Wollman static void
812c67b1d17SGarrett Wollman ipintr(void)
813c67b1d17SGarrett Wollman {
814c67b1d17SGarrett Wollman 	struct mbuf *m;
815c67b1d17SGarrett Wollman 
816c67b1d17SGarrett Wollman 	while (1) {
817c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
818c67b1d17SGarrett Wollman 		if (m == 0)
819c67b1d17SGarrett Wollman 			return;
820c67b1d17SGarrett Wollman 		ip_input(m);
821c67b1d17SGarrett Wollman 	}
822df8bae1dSRodney W. Grimes }
823df8bae1dSRodney W. Grimes 
824df8bae1dSRodney W. Grimes /*
8258948e4baSArchie Cobbs  * Take incoming datagram fragment and try to reassemble it into
8268948e4baSArchie Cobbs  * whole datagram.  If a chain for reassembly of this datagram already
8278948e4baSArchie Cobbs  * exists, then it is given as fp; otherwise have to make a chain.
8288948e4baSArchie Cobbs  *
8298948e4baSArchie Cobbs  * When IPDIVERT enabled, keep additional state with each packet that
8308948e4baSArchie Cobbs  * tells us if we need to divert or tee the packet we're building.
831df8bae1dSRodney W. Grimes  */
8328948e4baSArchie Cobbs 
8336a800098SYoshinobu Inoue static struct mbuf *
8348948e4baSArchie Cobbs #ifdef IPDIVERT
8358948e4baSArchie Cobbs ip_reass(m, fp, where, divinfo, divcookie)
8368948e4baSArchie Cobbs #else
8376effc713SDoug Rabson ip_reass(m, fp, where)
8388948e4baSArchie Cobbs #endif
8396effc713SDoug Rabson 	register struct mbuf *m;
840df8bae1dSRodney W. Grimes 	register struct ipq *fp;
841194a213eSAndrey A. Chernov 	struct   ipq    *where;
8428948e4baSArchie Cobbs #ifdef IPDIVERT
8438948e4baSArchie Cobbs 	u_int32_t *divinfo;
8448948e4baSArchie Cobbs 	u_int16_t *divcookie;
8458948e4baSArchie Cobbs #endif
846df8bae1dSRodney W. Grimes {
8476effc713SDoug Rabson 	struct ip *ip = mtod(m, struct ip *);
848b6ea1aa5SRuslan Ermilov 	register struct mbuf *p, *q, *nq;
849df8bae1dSRodney W. Grimes 	struct mbuf *t;
8506effc713SDoug Rabson 	int hlen = IP_VHL_HL(ip->ip_vhl) << 2;
851df8bae1dSRodney W. Grimes 	int i, next;
852df8bae1dSRodney W. Grimes 
853df8bae1dSRodney W. Grimes 	/*
854df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
855df8bae1dSRodney W. Grimes 	 * would confuse code below.
856df8bae1dSRodney W. Grimes 	 */
857df8bae1dSRodney W. Grimes 	m->m_data += hlen;
858df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
859df8bae1dSRodney W. Grimes 
860df8bae1dSRodney W. Grimes 	/*
861df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
862df8bae1dSRodney W. Grimes 	 */
863df8bae1dSRodney W. Grimes 	if (fp == 0) {
864df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
865df8bae1dSRodney W. Grimes 			goto dropfrag;
866df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
867194a213eSAndrey A. Chernov 		insque(fp, where);
868194a213eSAndrey A. Chernov 		nipq++;
869df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
870df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
871df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
8726effc713SDoug Rabson 		fp->ipq_src = ip->ip_src;
8736effc713SDoug Rabson 		fp->ipq_dst = ip->ip_dst;
874af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
875af38c68cSLuigi Rizzo 		m->m_nextpkt = NULL;
87693e0e116SJulian Elischer #ifdef IPDIVERT
8778948e4baSArchie Cobbs 		fp->ipq_div_info = 0;
878bb60f459SJulian Elischer 		fp->ipq_div_cookie = 0;
87993e0e116SJulian Elischer #endif
880af38c68cSLuigi Rizzo 		goto inserted;
881df8bae1dSRodney W. Grimes 	}
882df8bae1dSRodney W. Grimes 
8836effc713SDoug Rabson #define GETIP(m)	((struct ip*)((m)->m_pkthdr.header))
8846effc713SDoug Rabson 
885df8bae1dSRodney W. Grimes 	/*
886df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
887df8bae1dSRodney W. Grimes 	 */
8886effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
8896effc713SDoug Rabson 		if (GETIP(q)->ip_off > ip->ip_off)
890df8bae1dSRodney W. Grimes 			break;
891df8bae1dSRodney W. Grimes 
892df8bae1dSRodney W. Grimes 	/*
893df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
894df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
895af38c68cSLuigi Rizzo 	 * segment.  If it provides all of our data, drop us, otherwise
896af38c68cSLuigi Rizzo 	 * stick new segment in the proper place.
897db4f9cc7SJonathan Lemon 	 *
898db4f9cc7SJonathan Lemon 	 * If some of the data is dropped from the the preceding
899db4f9cc7SJonathan Lemon 	 * segment, then it's checksum is invalidated.
900df8bae1dSRodney W. Grimes 	 */
9016effc713SDoug Rabson 	if (p) {
9026effc713SDoug Rabson 		i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;
903df8bae1dSRodney W. Grimes 		if (i > 0) {
904df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
905df8bae1dSRodney W. Grimes 				goto dropfrag;
9066a800098SYoshinobu Inoue 			m_adj(m, i);
907db4f9cc7SJonathan Lemon 			m->m_pkthdr.csum_flags = 0;
908df8bae1dSRodney W. Grimes 			ip->ip_off += i;
909df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
910df8bae1dSRodney W. Grimes 		}
911af38c68cSLuigi Rizzo 		m->m_nextpkt = p->m_nextpkt;
912af38c68cSLuigi Rizzo 		p->m_nextpkt = m;
913af38c68cSLuigi Rizzo 	} else {
914af38c68cSLuigi Rizzo 		m->m_nextpkt = fp->ipq_frags;
915af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
916df8bae1dSRodney W. Grimes 	}
917df8bae1dSRodney W. Grimes 
918df8bae1dSRodney W. Grimes 	/*
919df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
920df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
921df8bae1dSRodney W. Grimes 	 */
9226effc713SDoug Rabson 	for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;
923af38c68cSLuigi Rizzo 	     q = nq) {
9246effc713SDoug Rabson 		i = (ip->ip_off + ip->ip_len) -
9256effc713SDoug Rabson 		    GETIP(q)->ip_off;
9266effc713SDoug Rabson 		if (i < GETIP(q)->ip_len) {
9276effc713SDoug Rabson 			GETIP(q)->ip_len -= i;
9286effc713SDoug Rabson 			GETIP(q)->ip_off += i;
9296effc713SDoug Rabson 			m_adj(q, i);
930db4f9cc7SJonathan Lemon 			q->m_pkthdr.csum_flags = 0;
931df8bae1dSRodney W. Grimes 			break;
932df8bae1dSRodney W. Grimes 		}
9336effc713SDoug Rabson 		nq = q->m_nextpkt;
934af38c68cSLuigi Rizzo 		m->m_nextpkt = nq;
9356effc713SDoug Rabson 		m_freem(q);
936df8bae1dSRodney W. Grimes 	}
937df8bae1dSRodney W. Grimes 
938af38c68cSLuigi Rizzo inserted:
93993e0e116SJulian Elischer 
94093e0e116SJulian Elischer #ifdef IPDIVERT
94193e0e116SJulian Elischer 	/*
9428948e4baSArchie Cobbs 	 * Transfer firewall instructions to the fragment structure.
9438948e4baSArchie Cobbs 	 * Any fragment diverting causes the whole packet to divert.
94493e0e116SJulian Elischer 	 */
9458948e4baSArchie Cobbs 	fp->ipq_div_info = *divinfo;
9468948e4baSArchie Cobbs 	fp->ipq_div_cookie = *divcookie;
9478948e4baSArchie Cobbs 	*divinfo = 0;
9488948e4baSArchie Cobbs 	*divcookie = 0;
94993e0e116SJulian Elischer #endif
95093e0e116SJulian Elischer 
951df8bae1dSRodney W. Grimes 	/*
952af38c68cSLuigi Rizzo 	 * Check for complete reassembly.
953df8bae1dSRodney W. Grimes 	 */
9546effc713SDoug Rabson 	next = 0;
9556effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
9566effc713SDoug Rabson 		if (GETIP(q)->ip_off != next)
9576effc713SDoug Rabson 			return (0);
9586effc713SDoug Rabson 		next += GETIP(q)->ip_len;
9596effc713SDoug Rabson 	}
9606effc713SDoug Rabson 	/* Make sure the last packet didn't have the IP_MF flag */
9616effc713SDoug Rabson 	if (p->m_flags & M_FRAG)
962df8bae1dSRodney W. Grimes 		return (0);
963df8bae1dSRodney W. Grimes 
964df8bae1dSRodney W. Grimes 	/*
965430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
966430d30d8SBill Fenner 	 */
9676effc713SDoug Rabson 	q = fp->ipq_frags;
9686effc713SDoug Rabson 	ip = GETIP(q);
9696effc713SDoug Rabson 	if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) {
970430d30d8SBill Fenner 		ipstat.ips_toolong++;
971430d30d8SBill Fenner 		ip_freef(fp);
972430d30d8SBill Fenner 		return (0);
973430d30d8SBill Fenner 	}
974430d30d8SBill Fenner 
975430d30d8SBill Fenner 	/*
976430d30d8SBill Fenner 	 * Concatenate fragments.
977df8bae1dSRodney W. Grimes 	 */
9786effc713SDoug Rabson 	m = q;
979df8bae1dSRodney W. Grimes 	t = m->m_next;
980df8bae1dSRodney W. Grimes 	m->m_next = 0;
981df8bae1dSRodney W. Grimes 	m_cat(m, t);
9826effc713SDoug Rabson 	nq = q->m_nextpkt;
983945aa40dSDoug Rabson 	q->m_nextpkt = 0;
9846effc713SDoug Rabson 	for (q = nq; q != NULL; q = nq) {
9856effc713SDoug Rabson 		nq = q->m_nextpkt;
986945aa40dSDoug Rabson 		q->m_nextpkt = NULL;
987db4f9cc7SJonathan Lemon 		m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;
988db4f9cc7SJonathan Lemon 		m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;
989a8db1d93SJonathan Lemon 		m_cat(m, q);
990df8bae1dSRodney W. Grimes 	}
991df8bae1dSRodney W. Grimes 
99293e0e116SJulian Elischer #ifdef IPDIVERT
99393e0e116SJulian Elischer 	/*
9948948e4baSArchie Cobbs 	 * Extract firewall instructions from the fragment structure.
99593e0e116SJulian Elischer 	 */
9968948e4baSArchie Cobbs 	*divinfo = fp->ipq_div_info;
9978948e4baSArchie Cobbs 	*divcookie = fp->ipq_div_cookie;
99893e0e116SJulian Elischer #endif
99993e0e116SJulian Elischer 
1000df8bae1dSRodney W. Grimes 	/*
1001df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
1002df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
1003df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
1004df8bae1dSRodney W. Grimes 	 * Make header visible.
1005df8bae1dSRodney W. Grimes 	 */
1006df8bae1dSRodney W. Grimes 	ip->ip_len = next;
10076effc713SDoug Rabson 	ip->ip_src = fp->ipq_src;
10086effc713SDoug Rabson 	ip->ip_dst = fp->ipq_dst;
1009df8bae1dSRodney W. Grimes 	remque(fp);
1010194a213eSAndrey A. Chernov 	nipq--;
1011df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
10126effc713SDoug Rabson 	m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
10136effc713SDoug Rabson 	m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);
1014df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
1015df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
1016df8bae1dSRodney W. Grimes 		register int plen = 0;
10176a800098SYoshinobu Inoue 		for (t = m; t; t = t->m_next)
10186a800098SYoshinobu Inoue 			plen += t->m_len;
10196a800098SYoshinobu Inoue 		m->m_pkthdr.len = plen;
1020df8bae1dSRodney W. Grimes 	}
10216a800098SYoshinobu Inoue 	return (m);
1022df8bae1dSRodney W. Grimes 
1023df8bae1dSRodney W. Grimes dropfrag:
1024efe39c6aSJulian Elischer #ifdef IPDIVERT
10258948e4baSArchie Cobbs 	*divinfo = 0;
10268948e4baSArchie Cobbs 	*divcookie = 0;
1027efe39c6aSJulian Elischer #endif
1028df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
1029df8bae1dSRodney W. Grimes 	m_freem(m);
1030df8bae1dSRodney W. Grimes 	return (0);
10316effc713SDoug Rabson 
10326effc713SDoug Rabson #undef GETIP
1033df8bae1dSRodney W. Grimes }
1034df8bae1dSRodney W. Grimes 
1035df8bae1dSRodney W. Grimes /*
1036df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
1037df8bae1dSRodney W. Grimes  * associated datagrams.
1038df8bae1dSRodney W. Grimes  */
10390312fbe9SPoul-Henning Kamp static void
1040df8bae1dSRodney W. Grimes ip_freef(fp)
1041df8bae1dSRodney W. Grimes 	struct ipq *fp;
1042df8bae1dSRodney W. Grimes {
10436effc713SDoug Rabson 	register struct mbuf *q;
1044df8bae1dSRodney W. Grimes 
10456effc713SDoug Rabson 	while (fp->ipq_frags) {
10466effc713SDoug Rabson 		q = fp->ipq_frags;
10476effc713SDoug Rabson 		fp->ipq_frags = q->m_nextpkt;
10486effc713SDoug Rabson 		m_freem(q);
1049df8bae1dSRodney W. Grimes 	}
1050df8bae1dSRodney W. Grimes 	remque(fp);
1051df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
1052194a213eSAndrey A. Chernov 	nipq--;
1053df8bae1dSRodney W. Grimes }
1054df8bae1dSRodney W. Grimes 
1055df8bae1dSRodney W. Grimes /*
1056df8bae1dSRodney W. Grimes  * IP timer processing;
1057df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
1058df8bae1dSRodney W. Grimes  * queue, discard it.
1059df8bae1dSRodney W. Grimes  */
1060df8bae1dSRodney W. Grimes void
1061df8bae1dSRodney W. Grimes ip_slowtimo()
1062df8bae1dSRodney W. Grimes {
1063df8bae1dSRodney W. Grimes 	register struct ipq *fp;
1064df8bae1dSRodney W. Grimes 	int s = splnet();
1065194a213eSAndrey A. Chernov 	int i;
1066df8bae1dSRodney W. Grimes 
1067194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
1068194a213eSAndrey A. Chernov 		fp = ipq[i].next;
1069194a213eSAndrey A. Chernov 		if (fp == 0)
1070194a213eSAndrey A. Chernov 			continue;
1071194a213eSAndrey A. Chernov 		while (fp != &ipq[i]) {
1072df8bae1dSRodney W. Grimes 			--fp->ipq_ttl;
1073df8bae1dSRodney W. Grimes 			fp = fp->next;
1074df8bae1dSRodney W. Grimes 			if (fp->prev->ipq_ttl == 0) {
1075df8bae1dSRodney W. Grimes 				ipstat.ips_fragtimeout++;
1076df8bae1dSRodney W. Grimes 				ip_freef(fp->prev);
1077df8bae1dSRodney W. Grimes 			}
1078df8bae1dSRodney W. Grimes 		}
1079194a213eSAndrey A. Chernov 	}
10801f91d8c5SDavid Greenman 	ipflow_slowtimo();
1081df8bae1dSRodney W. Grimes 	splx(s);
1082df8bae1dSRodney W. Grimes }
1083df8bae1dSRodney W. Grimes 
1084df8bae1dSRodney W. Grimes /*
1085df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
1086df8bae1dSRodney W. Grimes  */
1087df8bae1dSRodney W. Grimes void
1088df8bae1dSRodney W. Grimes ip_drain()
1089df8bae1dSRodney W. Grimes {
1090194a213eSAndrey A. Chernov 	int     i;
1091ce29ab3aSGarrett Wollman 
1092194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
1093194a213eSAndrey A. Chernov 		while (ipq[i].next != &ipq[i]) {
1094194a213eSAndrey A. Chernov 			ipstat.ips_fragdropped++;
1095194a213eSAndrey A. Chernov 			ip_freef(ipq[i].next);
1096194a213eSAndrey A. Chernov 		}
1097194a213eSAndrey A. Chernov 	}
1098ce29ab3aSGarrett Wollman 	in_rtqdrain();
1099df8bae1dSRodney W. Grimes }
1100df8bae1dSRodney W. Grimes 
1101df8bae1dSRodney W. Grimes /*
1102df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
1103df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
1104df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
1105df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
1106df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
1107df8bae1dSRodney W. Grimes  */
11080312fbe9SPoul-Henning Kamp static int
1109df8bae1dSRodney W. Grimes ip_dooptions(m)
1110df8bae1dSRodney W. Grimes 	struct mbuf *m;
1111df8bae1dSRodney W. Grimes {
1112df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1113df8bae1dSRodney W. Grimes 	register u_char *cp;
1114df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
1115df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
1116df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
1117df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
1118df8bae1dSRodney W. Grimes 	n_time ntime;
1119df8bae1dSRodney W. Grimes 
1120df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
1121df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
112258938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1123df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
1124df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
1125df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
1126df8bae1dSRodney W. Grimes 			break;
1127df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
1128df8bae1dSRodney W. Grimes 			optlen = 1;
1129df8bae1dSRodney W. Grimes 		else {
1130fdcb8debSJun-ichiro itojun Hagino 			if (cnt < IPOPT_OLEN + sizeof(*cp)) {
1131fdcb8debSJun-ichiro itojun Hagino 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1132fdcb8debSJun-ichiro itojun Hagino 				goto bad;
1133fdcb8debSJun-ichiro itojun Hagino 			}
1134df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
1135707d00a3SJonathan Lemon 			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {
1136df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1137df8bae1dSRodney W. Grimes 				goto bad;
1138df8bae1dSRodney W. Grimes 			}
1139df8bae1dSRodney W. Grimes 		}
1140df8bae1dSRodney W. Grimes 		switch (opt) {
1141df8bae1dSRodney W. Grimes 
1142df8bae1dSRodney W. Grimes 		default:
1143df8bae1dSRodney W. Grimes 			break;
1144df8bae1dSRodney W. Grimes 
1145df8bae1dSRodney W. Grimes 		/*
1146df8bae1dSRodney W. Grimes 		 * Source routing with record.
1147df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
1148df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
1149df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
1150df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
1151df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
1152df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
1153df8bae1dSRodney W. Grimes 		 */
1154df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
1155df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
1156df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1157df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1158df8bae1dSRodney W. Grimes 				goto bad;
1159df8bae1dSRodney W. Grimes 			}
1160df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
1161df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
1162df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
1163df8bae1dSRodney W. Grimes 			if (ia == 0) {
1164df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
1165df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
1166df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
1167df8bae1dSRodney W. Grimes 					goto bad;
1168df8bae1dSRodney W. Grimes 				}
1169bc189bf8SGuido van Rooij 				if (!ip_dosourceroute)
1170bc189bf8SGuido van Rooij 					goto nosourcerouting;
1171df8bae1dSRodney W. Grimes 				/*
1172df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
1173df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
1174df8bae1dSRodney W. Grimes 				 */
1175df8bae1dSRodney W. Grimes 				break;
1176df8bae1dSRodney W. Grimes 			}
1177df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
11785d5d5fc0SJonathan Lemon 			if (off > optlen - (int)sizeof(struct in_addr)) {
1179df8bae1dSRodney W. Grimes 				/*
1180df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
1181df8bae1dSRodney W. Grimes 				 */
11824fce5804SGuido van Rooij 				if (!ip_acceptsourceroute)
11834fce5804SGuido van Rooij 					goto nosourcerouting;
1184df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
1185df8bae1dSRodney W. Grimes 				break;
1186df8bae1dSRodney W. Grimes 			}
11871025071fSGarrett Wollman 
11881025071fSGarrett Wollman 			if (!ip_dosourceroute) {
11890af8d3ecSDavid Greenman 				if (ipforwarding) {
11900af8d3ecSDavid Greenman 					char buf[16]; /* aaa.bbb.ccc.ddd\0 */
11910af8d3ecSDavid Greenman 					/*
11920af8d3ecSDavid Greenman 					 * Acting as a router, so generate ICMP
11930af8d3ecSDavid Greenman 					 */
1194efa48587SGuido van Rooij nosourcerouting:
1195bc189bf8SGuido van Rooij 					strcpy(buf, inet_ntoa(ip->ip_dst));
11961025071fSGarrett Wollman 					log(LOG_WARNING,
11971025071fSGarrett Wollman 					    "attempted source route from %s to %s\n",
11981025071fSGarrett Wollman 					    inet_ntoa(ip->ip_src), buf);
11991025071fSGarrett Wollman 					type = ICMP_UNREACH;
12001025071fSGarrett Wollman 					code = ICMP_UNREACH_SRCFAIL;
12011025071fSGarrett Wollman 					goto bad;
12020af8d3ecSDavid Greenman 				} else {
12030af8d3ecSDavid Greenman 					/*
12040af8d3ecSDavid Greenman 					 * Not acting as a router, so silently drop.
12050af8d3ecSDavid Greenman 					 */
12060af8d3ecSDavid Greenman 					ipstat.ips_cantforward++;
12070af8d3ecSDavid Greenman 					m_freem(m);
12080af8d3ecSDavid Greenman 					return (1);
12090af8d3ecSDavid Greenman 				}
12101025071fSGarrett Wollman 			}
12111025071fSGarrett Wollman 
1212df8bae1dSRodney W. Grimes 			/*
1213df8bae1dSRodney W. Grimes 			 * locate outgoing interface
1214df8bae1dSRodney W. Grimes 			 */
121594a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
1216df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
12171025071fSGarrett Wollman 
1218df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
1219df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
1220df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
1221df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
1222df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
1223df8bae1dSRodney W. Grimes 			} else
1224df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
1225df8bae1dSRodney W. Grimes 			if (ia == 0) {
1226df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1227df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
1228df8bae1dSRodney W. Grimes 				goto bad;
1229df8bae1dSRodney W. Grimes 			}
1230df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
123194a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
123294a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1233df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1234df8bae1dSRodney W. Grimes 			/*
1235df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
1236df8bae1dSRodney W. Grimes 			 */
1237df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1238df8bae1dSRodney W. Grimes 			break;
1239df8bae1dSRodney W. Grimes 
1240df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1241707d00a3SJonathan Lemon 			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
1242707d00a3SJonathan Lemon 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1243707d00a3SJonathan Lemon 				goto bad;
1244707d00a3SJonathan Lemon 			}
1245df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1246df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1247df8bae1dSRodney W. Grimes 				goto bad;
1248df8bae1dSRodney W. Grimes 			}
1249df8bae1dSRodney W. Grimes 			/*
1250df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1251df8bae1dSRodney W. Grimes 			 */
1252df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
12535d5d5fc0SJonathan Lemon 			if (off > optlen - (int)sizeof(struct in_addr))
1254df8bae1dSRodney W. Grimes 				break;
125594a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1256df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1257df8bae1dSRodney W. Grimes 			/*
1258df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1259df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1260df8bae1dSRodney W. Grimes 			 */
1261df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
1262df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
1263df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1264df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1265df8bae1dSRodney W. Grimes 				goto bad;
1266df8bae1dSRodney W. Grimes 			}
126794a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
126894a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1269df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1270df8bae1dSRodney W. Grimes 			break;
1271df8bae1dSRodney W. Grimes 
1272df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1273df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
1274df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
1275df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
1276df8bae1dSRodney W. Grimes 				goto bad;
12775d5d5fc0SJonathan Lemon 			if (ipt->ipt_ptr >
12785d5d5fc0SJonathan Lemon 			    ipt->ipt_len - (int)sizeof(int32_t)) {
1279df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
1280df8bae1dSRodney W. Grimes 					goto bad;
1281df8bae1dSRodney W. Grimes 				break;
1282df8bae1dSRodney W. Grimes 			}
1283df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
1284df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
1285df8bae1dSRodney W. Grimes 
1286df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1287df8bae1dSRodney W. Grimes 				break;
1288df8bae1dSRodney W. Grimes 
1289df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
1290b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1291df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1292df8bae1dSRodney W. Grimes 					goto bad;
1293df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1294df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1295df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
1296df8bae1dSRodney W. Grimes 				if (ia == 0)
1297df8bae1dSRodney W. Grimes 					continue;
129894a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
129994a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
1300df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1301df8bae1dSRodney W. Grimes 				break;
1302df8bae1dSRodney W. Grimes 
1303df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1304b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1305df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1306df8bae1dSRodney W. Grimes 					goto bad;
130794a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1308df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1309df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1310df8bae1dSRodney W. Grimes 					continue;
1311df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1312df8bae1dSRodney W. Grimes 				break;
1313df8bae1dSRodney W. Grimes 
1314df8bae1dSRodney W. Grimes 			default:
1315df8bae1dSRodney W. Grimes 				goto bad;
1316df8bae1dSRodney W. Grimes 			}
1317df8bae1dSRodney W. Grimes 			ntime = iptime();
131894a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1319df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1320df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1321df8bae1dSRodney W. Grimes 		}
1322df8bae1dSRodney W. Grimes 	}
132347174b49SAndrey A. Chernov 	if (forward && ipforwarding) {
1324df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1325df8bae1dSRodney W. Grimes 		return (1);
1326df8bae1dSRodney W. Grimes 	}
1327df8bae1dSRodney W. Grimes 	return (0);
1328df8bae1dSRodney W. Grimes bad:
1329df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1330df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1331df8bae1dSRodney W. Grimes 	return (1);
1332df8bae1dSRodney W. Grimes }
1333df8bae1dSRodney W. Grimes 
1334df8bae1dSRodney W. Grimes /*
1335df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1336df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1337df8bae1dSRodney W. Grimes  */
13380312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1339df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1340df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1341df8bae1dSRodney W. Grimes {
1342df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1343df8bae1dSRodney W. Grimes 
1344df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1345df8bae1dSRodney W. Grimes 
1346df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1347df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1348df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1349df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1350df8bae1dSRodney W. Grimes 		}
1351df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1352df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1353df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1354df8bae1dSRodney W. Grimes 
13552c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1356df8bae1dSRodney W. Grimes 	}
1357df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1358df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1359df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1360df8bae1dSRodney W. Grimes }
1361df8bae1dSRodney W. Grimes 
1362df8bae1dSRodney W. Grimes /*
1363df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1364df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1365df8bae1dSRodney W. Grimes  */
1366df8bae1dSRodney W. Grimes void
1367df8bae1dSRodney W. Grimes save_rte(option, dst)
1368df8bae1dSRodney W. Grimes 	u_char *option;
1369df8bae1dSRodney W. Grimes 	struct in_addr dst;
1370df8bae1dSRodney W. Grimes {
1371df8bae1dSRodney W. Grimes 	unsigned olen;
1372df8bae1dSRodney W. Grimes 
1373df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1374df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1375df8bae1dSRodney W. Grimes 	if (ipprintfs)
1376df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1377df8bae1dSRodney W. Grimes #endif
1378df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1379df8bae1dSRodney W. Grimes 		return;
13800453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1381df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1382df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1383df8bae1dSRodney W. Grimes }
1384df8bae1dSRodney W. Grimes 
1385df8bae1dSRodney W. Grimes /*
1386df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1387df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1388df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1389df8bae1dSRodney W. Grimes  */
1390df8bae1dSRodney W. Grimes struct mbuf *
1391df8bae1dSRodney W. Grimes ip_srcroute()
1392df8bae1dSRodney W. Grimes {
1393df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1394df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1395df8bae1dSRodney W. Grimes 
1396df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1397df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1398cfe8b629SGarrett Wollman 	m = m_get(M_DONTWAIT, MT_HEADER);
1399df8bae1dSRodney W. Grimes 	if (m == 0)
1400df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1401df8bae1dSRodney W. Grimes 
1402df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1403df8bae1dSRodney W. Grimes 
1404df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1405df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1406df8bae1dSRodney W. Grimes 	    OPTSIZ;
1407df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1408df8bae1dSRodney W. Grimes 	if (ipprintfs)
1409df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1410df8bae1dSRodney W. Grimes #endif
1411df8bae1dSRodney W. Grimes 
1412df8bae1dSRodney W. Grimes 	/*
1413df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1414df8bae1dSRodney W. Grimes 	 */
1415df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1416df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1417df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1418df8bae1dSRodney W. Grimes 	if (ipprintfs)
1419af38c68cSLuigi Rizzo 		printf(" hops %lx", (u_long)ntohl(mtod(m, struct in_addr *)->s_addr));
1420df8bae1dSRodney W. Grimes #endif
1421df8bae1dSRodney W. Grimes 
1422df8bae1dSRodney W. Grimes 	/*
1423df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1424df8bae1dSRodney W. Grimes 	 */
1425df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1426df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
142794a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
142894a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1429df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1430df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1431df8bae1dSRodney W. Grimes #undef OPTSIZ
1432df8bae1dSRodney W. Grimes 	/*
1433df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1434df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1435df8bae1dSRodney W. Grimes 	 */
1436df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1437df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1438df8bae1dSRodney W. Grimes 		if (ipprintfs)
1439af38c68cSLuigi Rizzo 			printf(" %lx", (u_long)ntohl(q->s_addr));
1440df8bae1dSRodney W. Grimes #endif
1441df8bae1dSRodney W. Grimes 		*q++ = *p--;
1442df8bae1dSRodney W. Grimes 	}
1443df8bae1dSRodney W. Grimes 	/*
1444df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1445df8bae1dSRodney W. Grimes 	 */
1446df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1447df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1448df8bae1dSRodney W. Grimes 	if (ipprintfs)
1449af38c68cSLuigi Rizzo 		printf(" %lx\n", (u_long)ntohl(q->s_addr));
1450df8bae1dSRodney W. Grimes #endif
1451df8bae1dSRodney W. Grimes 	return (m);
1452df8bae1dSRodney W. Grimes }
1453df8bae1dSRodney W. Grimes 
1454df8bae1dSRodney W. Grimes /*
1455df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1456df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1457df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1458df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1459df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1460df8bae1dSRodney W. Grimes  */
1461df8bae1dSRodney W. Grimes void
1462df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1463df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1464df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1465df8bae1dSRodney W. Grimes {
1466df8bae1dSRodney W. Grimes 	register int i;
1467df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1468df8bae1dSRodney W. Grimes 	register caddr_t opts;
1469df8bae1dSRodney W. Grimes 	int olen;
1470df8bae1dSRodney W. Grimes 
147158938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1472df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1473df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1474df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1475df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1476df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1477df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
147858938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1479df8bae1dSRodney W. Grimes }
1480df8bae1dSRodney W. Grimes 
1481df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1482df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1483df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1484df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1485df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1486df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1487e4bb5b05SJonathan Lemon 	ENOPROTOOPT,	ENETRESET
1488df8bae1dSRodney W. Grimes };
1489df8bae1dSRodney W. Grimes 
1490df8bae1dSRodney W. Grimes /*
1491df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1492df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1493df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1494df8bae1dSRodney W. Grimes  * of codes and types.
1495df8bae1dSRodney W. Grimes  *
1496df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1497df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1498df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1499df8bae1dSRodney W. Grimes  * protocol deal with that.
1500df8bae1dSRodney W. Grimes  *
1501df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1502df8bae1dSRodney W. Grimes  * via a source route.
1503df8bae1dSRodney W. Grimes  */
15040312fbe9SPoul-Henning Kamp static void
1505df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1506df8bae1dSRodney W. Grimes 	struct mbuf *m;
1507df8bae1dSRodney W. Grimes 	int srcrt;
1508df8bae1dSRodney W. Grimes {
1509df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1510df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1511df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
151226f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1513df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1514df8bae1dSRodney W. Grimes 	n_long dest;
1515df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
15166a800098SYoshinobu Inoue #ifdef IPSEC
15176a800098SYoshinobu Inoue 	struct ifnet dummyifp;
15186a800098SYoshinobu Inoue #endif
1519df8bae1dSRodney W. Grimes 
1520df8bae1dSRodney W. Grimes 	dest = 0;
1521df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1522df8bae1dSRodney W. Grimes 	if (ipprintfs)
152361ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1524162886e2SBruce Evans 		    (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr,
1525162886e2SBruce Evans 		    ip->ip_ttl);
1526df8bae1dSRodney W. Grimes #endif
1527100ba1a6SJordan K. Hubbard 
1528100ba1a6SJordan K. Hubbard 
152992af003dSGarrett Wollman 	if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) {
1530df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1531df8bae1dSRodney W. Grimes 		m_freem(m);
1532df8bae1dSRodney W. Grimes 		return;
1533df8bae1dSRodney W. Grimes 	}
15341b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
15351b968362SDag-Erling Smørgrav 	if (!ipstealth) {
15361b968362SDag-Erling Smørgrav #endif
1537df8bae1dSRodney W. Grimes 		if (ip->ip_ttl <= IPTTLDEC) {
15381b968362SDag-Erling Smørgrav 			icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
15391b968362SDag-Erling Smørgrav 			    dest, 0);
1540df8bae1dSRodney W. Grimes 			return;
1541df8bae1dSRodney W. Grimes 		}
15421b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
15431b968362SDag-Erling Smørgrav 	}
15441b968362SDag-Erling Smørgrav #endif
1545df8bae1dSRodney W. Grimes 
1546df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1547df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1548df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1549df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1550df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1551df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1552df8bae1dSRodney W. Grimes 		}
1553df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1554df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1555df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1556df8bae1dSRodney W. Grimes 
15572c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1558df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1559df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1560df8bae1dSRodney W. Grimes 			return;
1561df8bae1dSRodney W. Grimes 		}
1562df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1563df8bae1dSRodney W. Grimes 	}
1564df8bae1dSRodney W. Grimes 
1565df8bae1dSRodney W. Grimes 	/*
1566bfef7ed4SIan Dowse 	 * Save the IP header and at most 8 bytes of the payload,
1567bfef7ed4SIan Dowse 	 * in case we need to generate an ICMP message to the src.
1568bfef7ed4SIan Dowse 	 *
1569bfef7ed4SIan Dowse 	 * We don't use m_copy() because it might return a reference
1570bfef7ed4SIan Dowse 	 * to a shared cluster. Both this function and ip_output()
1571bfef7ed4SIan Dowse 	 * assume exclusive access to the IP header in `m', so any
1572bfef7ed4SIan Dowse 	 * data in a cluster may change before we reach icmp_error().
1573df8bae1dSRodney W. Grimes 	 */
1574bfef7ed4SIan Dowse 	MGET(mcopy, M_DONTWAIT, m->m_type);
1575bfef7ed4SIan Dowse 	if (mcopy != NULL) {
1576bfef7ed4SIan Dowse 		M_COPY_PKTHDR(mcopy, m);
1577bfef7ed4SIan Dowse 		mcopy->m_len = imin((IP_VHL_HL(ip->ip_vhl) << 2) + 8,
1578bfef7ed4SIan Dowse 		    (int)ip->ip_len);
1579bfef7ed4SIan Dowse 		m_copydata(m, 0, mcopy->m_len, mtod(mcopy, caddr_t));
1580bfef7ed4SIan Dowse 	}
158104287599SRuslan Ermilov 
158204287599SRuslan Ermilov #ifdef IPSTEALTH
158304287599SRuslan Ermilov 	if (!ipstealth) {
158404287599SRuslan Ermilov #endif
158504287599SRuslan Ermilov 		ip->ip_ttl -= IPTTLDEC;
158604287599SRuslan Ermilov #ifdef IPSTEALTH
158704287599SRuslan Ermilov 	}
158804287599SRuslan Ermilov #endif
1589df8bae1dSRodney W. Grimes 
1590df8bae1dSRodney W. Grimes 	/*
1591df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1592df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1593df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1594df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1595df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1596df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1597df8bae1dSRodney W. Grimes 	 */
1598df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1599df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1600df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1601df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1602df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1603df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1604df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1605df8bae1dSRodney W. Grimes 
1606df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1607df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1608df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1609df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1610df8bae1dSRodney W. Grimes 		    else
1611df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1612df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1613df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1614df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1615df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1616df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1617df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1618df8bae1dSRodney W. Grimes #endif
1619df8bae1dSRodney W. Grimes 		}
1620df8bae1dSRodney W. Grimes 	}
1621df8bae1dSRodney W. Grimes 
1622b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1623b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1624df8bae1dSRodney W. Grimes 	if (error)
1625df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1626df8bae1dSRodney W. Grimes 	else {
1627df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1628df8bae1dSRodney W. Grimes 		if (type)
1629df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1630df8bae1dSRodney W. Grimes 		else {
16311f91d8c5SDavid Greenman 			if (mcopy) {
16321f91d8c5SDavid Greenman 				ipflow_create(&ipforward_rt, mcopy);
1633df8bae1dSRodney W. Grimes 				m_freem(mcopy);
16341f91d8c5SDavid Greenman 			}
1635df8bae1dSRodney W. Grimes 			return;
1636df8bae1dSRodney W. Grimes 		}
1637df8bae1dSRodney W. Grimes 	}
1638df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1639df8bae1dSRodney W. Grimes 		return;
1640df8bae1dSRodney W. Grimes 	destifp = NULL;
1641df8bae1dSRodney W. Grimes 
1642df8bae1dSRodney W. Grimes 	switch (error) {
1643df8bae1dSRodney W. Grimes 
1644df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1645df8bae1dSRodney W. Grimes 		/* type, code set above */
1646df8bae1dSRodney W. Grimes 		break;
1647df8bae1dSRodney W. Grimes 
1648df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1649df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1650df8bae1dSRodney W. Grimes 	case ENETDOWN:
1651df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1652df8bae1dSRodney W. Grimes 	default:
1653df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1654df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1655df8bae1dSRodney W. Grimes 		break;
1656df8bae1dSRodney W. Grimes 
1657df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1658df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1659df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
16606a800098SYoshinobu Inoue #ifndef IPSEC
1661df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1662df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
16636a800098SYoshinobu Inoue #else
16646a800098SYoshinobu Inoue 		/*
16656a800098SYoshinobu Inoue 		 * If the packet is routed over IPsec tunnel, tell the
16666a800098SYoshinobu Inoue 		 * originator the tunnel MTU.
16676a800098SYoshinobu Inoue 		 *	tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
16686a800098SYoshinobu Inoue 		 * XXX quickhack!!!
16696a800098SYoshinobu Inoue 		 */
16706a800098SYoshinobu Inoue 		if (ipforward_rt.ro_rt) {
16716a800098SYoshinobu Inoue 			struct secpolicy *sp = NULL;
16726a800098SYoshinobu Inoue 			int ipsecerror;
16736a800098SYoshinobu Inoue 			int ipsechdr;
16746a800098SYoshinobu Inoue 			struct route *ro;
16756a800098SYoshinobu Inoue 
16766a800098SYoshinobu Inoue 			sp = ipsec4_getpolicybyaddr(mcopy,
16776a800098SYoshinobu Inoue 						    IPSEC_DIR_OUTBOUND,
16786a800098SYoshinobu Inoue 			                            IP_FORWARDING,
16796a800098SYoshinobu Inoue 			                            &ipsecerror);
16806a800098SYoshinobu Inoue 
16816a800098SYoshinobu Inoue 			if (sp == NULL)
16826a800098SYoshinobu Inoue 				destifp = ipforward_rt.ro_rt->rt_ifp;
16836a800098SYoshinobu Inoue 			else {
16846a800098SYoshinobu Inoue 				/* count IPsec header size */
16856a800098SYoshinobu Inoue 				ipsechdr = ipsec4_hdrsiz(mcopy,
16866a800098SYoshinobu Inoue 							 IPSEC_DIR_OUTBOUND,
16876a800098SYoshinobu Inoue 							 NULL);
16886a800098SYoshinobu Inoue 
16896a800098SYoshinobu Inoue 				/*
16906a800098SYoshinobu Inoue 				 * find the correct route for outer IPv4
16916a800098SYoshinobu Inoue 				 * header, compute tunnel MTU.
16926a800098SYoshinobu Inoue 				 *
16936a800098SYoshinobu Inoue 				 * XXX BUG ALERT
16946a800098SYoshinobu Inoue 				 * The "dummyifp" code relies upon the fact
16956a800098SYoshinobu Inoue 				 * that icmp_error() touches only ifp->if_mtu.
16966a800098SYoshinobu Inoue 				 */
16976a800098SYoshinobu Inoue 				/*XXX*/
16986a800098SYoshinobu Inoue 				destifp = NULL;
16996a800098SYoshinobu Inoue 				if (sp->req != NULL
17006a800098SYoshinobu Inoue 				 && sp->req->sav != NULL
17016a800098SYoshinobu Inoue 				 && sp->req->sav->sah != NULL) {
17026a800098SYoshinobu Inoue 					ro = &sp->req->sav->sah->sa_route;
17036a800098SYoshinobu Inoue 					if (ro->ro_rt && ro->ro_rt->rt_ifp) {
17046a800098SYoshinobu Inoue 						dummyifp.if_mtu =
17056a800098SYoshinobu Inoue 						    ro->ro_rt->rt_ifp->if_mtu;
17066a800098SYoshinobu Inoue 						dummyifp.if_mtu -= ipsechdr;
17076a800098SYoshinobu Inoue 						destifp = &dummyifp;
17086a800098SYoshinobu Inoue 					}
17096a800098SYoshinobu Inoue 				}
17106a800098SYoshinobu Inoue 
17116a800098SYoshinobu Inoue 				key_freesp(sp);
17126a800098SYoshinobu Inoue 			}
17136a800098SYoshinobu Inoue 		}
17146a800098SYoshinobu Inoue #endif /*IPSEC*/
1715df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1716df8bae1dSRodney W. Grimes 		break;
1717df8bae1dSRodney W. Grimes 
1718df8bae1dSRodney W. Grimes 	case ENOBUFS:
1719df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1720df8bae1dSRodney W. Grimes 		code = 0;
1721df8bae1dSRodney W. Grimes 		break;
17223a06e3e0SRuslan Ermilov 
17233a06e3e0SRuslan Ermilov 	case EACCES:			/* ipfw denied packet */
17243a06e3e0SRuslan Ermilov 		m_freem(mcopy);
17253a06e3e0SRuslan Ermilov 		return;
1726df8bae1dSRodney W. Grimes 	}
1727df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1728df8bae1dSRodney W. Grimes }
1729df8bae1dSRodney W. Grimes 
173082c23ebaSBill Fenner void
173182c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
173282c23ebaSBill Fenner 	register struct inpcb *inp;
173382c23ebaSBill Fenner 	register struct mbuf **mp;
173482c23ebaSBill Fenner 	register struct ip *ip;
173582c23ebaSBill Fenner 	register struct mbuf *m;
173682c23ebaSBill Fenner {
173782c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
173882c23ebaSBill Fenner 		struct timeval tv;
173982c23ebaSBill Fenner 
174082c23ebaSBill Fenner 		microtime(&tv);
174182c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
174282c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
174382c23ebaSBill Fenner 		if (*mp)
174482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
174582c23ebaSBill Fenner 	}
174682c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
174782c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
174882c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
174982c23ebaSBill Fenner 		if (*mp)
175082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
175182c23ebaSBill Fenner 	}
175282c23ebaSBill Fenner #ifdef notyet
175382c23ebaSBill Fenner 	/* XXX
175482c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
175582c23ebaSBill Fenner 	 * than they already were.
175682c23ebaSBill Fenner 	 */
175782c23ebaSBill Fenner 	/* options were tossed already */
175882c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
175982c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
176082c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
176182c23ebaSBill Fenner 		if (*mp)
176282c23ebaSBill Fenner 			mp = &(*mp)->m_next;
176382c23ebaSBill Fenner 	}
176482c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
176582c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
176682c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
176782c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
176882c23ebaSBill Fenner 		if (*mp)
176982c23ebaSBill Fenner 			mp = &(*mp)->m_next;
177082c23ebaSBill Fenner 	}
177182c23ebaSBill Fenner #endif
177282c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
1773d314ad7bSJulian Elischer 		struct ifnet *ifp;
1774d314ad7bSJulian Elischer 		struct sdlbuf {
177582c23ebaSBill Fenner 			struct sockaddr_dl sdl;
1776d314ad7bSJulian Elischer 			u_char	pad[32];
1777d314ad7bSJulian Elischer 		} sdlbuf;
1778d314ad7bSJulian Elischer 		struct sockaddr_dl *sdp;
1779d314ad7bSJulian Elischer 		struct sockaddr_dl *sdl2 = &sdlbuf.sdl;
178082c23ebaSBill Fenner 
1781d314ad7bSJulian Elischer 		if (((ifp = m->m_pkthdr.rcvif))
1782d314ad7bSJulian Elischer 		&& ( ifp->if_index && (ifp->if_index <= if_index))) {
1783d314ad7bSJulian Elischer 			sdp = (struct sockaddr_dl *)(ifnet_addrs
1784d314ad7bSJulian Elischer 					[ifp->if_index - 1]->ifa_addr);
1785d314ad7bSJulian Elischer 			/*
1786d314ad7bSJulian Elischer 			 * Change our mind and don't try copy.
1787d314ad7bSJulian Elischer 			 */
1788d314ad7bSJulian Elischer 			if ((sdp->sdl_family != AF_LINK)
1789d314ad7bSJulian Elischer 			|| (sdp->sdl_len > sizeof(sdlbuf))) {
1790d314ad7bSJulian Elischer 				goto makedummy;
1791d314ad7bSJulian Elischer 			}
1792d314ad7bSJulian Elischer 			bcopy(sdp, sdl2, sdp->sdl_len);
1793d314ad7bSJulian Elischer 		} else {
1794d314ad7bSJulian Elischer makedummy:
1795d314ad7bSJulian Elischer 			sdl2->sdl_len
1796d314ad7bSJulian Elischer 				= offsetof(struct sockaddr_dl, sdl_data[0]);
1797d314ad7bSJulian Elischer 			sdl2->sdl_family = AF_LINK;
1798d314ad7bSJulian Elischer 			sdl2->sdl_index = 0;
1799d314ad7bSJulian Elischer 			sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0;
1800d314ad7bSJulian Elischer 		}
1801d314ad7bSJulian Elischer 		*mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len,
180282c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
180382c23ebaSBill Fenner 		if (*mp)
180482c23ebaSBill Fenner 			mp = &(*mp)->m_next;
180582c23ebaSBill Fenner 	}
180682c23ebaSBill Fenner }
180782c23ebaSBill Fenner 
1808df8bae1dSRodney W. Grimes int
1809f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1810f0068c4aSGarrett Wollman {
1811f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1812f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1813f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1814f0068c4aSGarrett Wollman 
1815f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1816f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1817f0068c4aSGarrett Wollman 
1818f0068c4aSGarrett Wollman 	ip_rsvpd = so;
18191c5de19aSGarrett Wollman 	/*
18201c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
18211c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
18221c5de19aSGarrett Wollman 	 */
18231c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
18241c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
18251c5de19aSGarrett Wollman 		rsvp_on++;
18261c5de19aSGarrett Wollman 	}
1827f0068c4aSGarrett Wollman 
1828f0068c4aSGarrett Wollman 	return 0;
1829f0068c4aSGarrett Wollman }
1830f0068c4aSGarrett Wollman 
1831f0068c4aSGarrett Wollman int
1832f0068c4aSGarrett Wollman ip_rsvp_done(void)
1833f0068c4aSGarrett Wollman {
1834f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
18351c5de19aSGarrett Wollman 	/*
18361c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
18371c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
18381c5de19aSGarrett Wollman 	 */
18391c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
18401c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
18411c5de19aSGarrett Wollman 		rsvp_on--;
18421c5de19aSGarrett Wollman 	}
1843f0068c4aSGarrett Wollman 	return 0;
1844f0068c4aSGarrett Wollman }
1845