xref: /freebsd/sys/netinet/ip_input.c (revision b3e95d4ed09e497b778584ac12ceea158c504e72)
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 
127b3e95d4eSJonathan Lemon static int	ip_checkinterface = 1;
128b3e95d4eSJonathan Lemon SYSCTL_INT(_net_inet_ip, OID_AUTO, check_interface, CTLFLAG_RW,
129b3e95d4eSJonathan Lemon     &ip_checkinterface, 0, "Verify packet arrives on correct interface");
130b3e95d4eSJonathan Lemon 
131df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1320312fbe9SPoul-Henning Kamp static int	ipprintfs = 0;
133df8bae1dSRodney W. Grimes #endif
134df8bae1dSRodney W. Grimes 
135df8bae1dSRodney W. Grimes extern	struct domain inetdomain;
1366a800098SYoshinobu Inoue extern	struct ipprotosw inetsw[];
137df8bae1dSRodney W. Grimes u_char	ip_protox[IPPROTO_MAX];
1380312fbe9SPoul-Henning Kamp static int	ipqmaxlen = IFQ_MAXLEN;
13959562606SGarrett Wollman struct	in_ifaddrhead in_ifaddrhead; /* first inet address */
140afed1375SDavid Greenman SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RW,
1413d177f46SBill Fumerola     &ipintrq.ifq_maxlen, 0, "Maximum size of the IP input queue");
1420312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
1433d177f46SBill Fumerola     &ipintrq.ifq_drops, 0, "Number of packets dropped from the IP input queue");
144df8bae1dSRodney W. Grimes 
145f23b4c91SGarrett Wollman struct ipstat ipstat;
1466fce01c9SGarrett Wollman SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RD,
1473d177f46SBill Fumerola     &ipstat, ipstat, "IP statistics (struct ipstat, netinet/ip_var.h)");
148194a213eSAndrey A. Chernov 
149194a213eSAndrey A. Chernov /* Packet reassembly stuff */
150194a213eSAndrey A. Chernov #define IPREASS_NHASH_LOG2      6
151194a213eSAndrey A. Chernov #define IPREASS_NHASH           (1 << IPREASS_NHASH_LOG2)
152194a213eSAndrey A. Chernov #define IPREASS_HMASK           (IPREASS_NHASH - 1)
153194a213eSAndrey A. Chernov #define IPREASS_HASH(x,y) \
154831a80b0SMatthew Dillon 	(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
155194a213eSAndrey A. Chernov 
156194a213eSAndrey A. Chernov static struct ipq ipq[IPREASS_NHASH];
157194a213eSAndrey A. Chernov static int    nipq = 0;         /* total # of reass queues */
158194a213eSAndrey A. Chernov static int    maxnipq;
159367d34f8SBrian Somers const  int    ipintrq_present = 1;
160f23b4c91SGarrett Wollman 
1610312fbe9SPoul-Henning Kamp #ifdef IPCTL_DEFMTU
1620312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
1633d177f46SBill Fumerola     &ip_mtu, 0, "Default MTU");
1640312fbe9SPoul-Henning Kamp #endif
1650312fbe9SPoul-Henning Kamp 
1661b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
1671b968362SDag-Erling Smørgrav static int	ipstealth = 0;
1681b968362SDag-Erling Smørgrav SYSCTL_INT(_net_inet_ip, OID_AUTO, stealth, CTLFLAG_RW,
1691b968362SDag-Erling Smørgrav     &ipstealth, 0, "");
1701b968362SDag-Erling Smørgrav #endif
1711b968362SDag-Erling Smørgrav 
172cfe8b629SGarrett Wollman 
17323bf9953SPoul-Henning Kamp /* Firewall hooks */
17423bf9953SPoul-Henning Kamp ip_fw_chk_t *ip_fw_chk_ptr;
17523bf9953SPoul-Henning Kamp ip_fw_ctl_t *ip_fw_ctl_ptr;
1769fcc0795SLuigi Rizzo int fw_enable = 1 ;
177e7319babSPoul-Henning Kamp 
178b715f178SLuigi Rizzo #ifdef DUMMYNET
179b715f178SLuigi Rizzo ip_dn_ctl_t *ip_dn_ctl_ptr;
180b715f178SLuigi Rizzo #endif
181b715f178SLuigi Rizzo 
182afed1b49SDarren Reed 
183e7319babSPoul-Henning Kamp /*
184df8bae1dSRodney W. Grimes  * We need to save the IP options in case a protocol wants to respond
185df8bae1dSRodney W. Grimes  * to an incoming packet over the same route if the packet got here
186df8bae1dSRodney W. Grimes  * using IP source routing.  This allows connection establishment and
187df8bae1dSRodney W. Grimes  * maintenance when the remote end is on a network that is not known
188df8bae1dSRodney W. Grimes  * to us.
189df8bae1dSRodney W. Grimes  */
1900312fbe9SPoul-Henning Kamp static int	ip_nhops = 0;
191df8bae1dSRodney W. Grimes static	struct ip_srcrt {
192df8bae1dSRodney W. Grimes 	struct	in_addr dst;			/* final destination */
193df8bae1dSRodney W. Grimes 	char	nop;				/* one NOP to align */
194df8bae1dSRodney W. Grimes 	char	srcopt[IPOPT_OFFSET + 1];	/* OPTVAL, OLEN and OFFSET */
195df8bae1dSRodney W. Grimes 	struct	in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
196df8bae1dSRodney W. Grimes } ip_srcrt;
197df8bae1dSRodney W. Grimes 
198f9e354dfSJulian Elischer struct sockaddr_in *ip_fw_fwd_addr;
199f9e354dfSJulian Elischer 
200df8bae1dSRodney W. Grimes static void	save_rte __P((u_char *, struct in_addr));
2010312fbe9SPoul-Henning Kamp static int	ip_dooptions __P((struct mbuf *));
2020312fbe9SPoul-Henning Kamp static void	ip_forward __P((struct mbuf *, int));
2030312fbe9SPoul-Henning Kamp static void	ip_freef __P((struct ipq *));
2048948e4baSArchie Cobbs #ifdef IPDIVERT
2056a800098SYoshinobu Inoue static struct	mbuf *ip_reass __P((struct mbuf *,
2068948e4baSArchie Cobbs 			struct ipq *, struct ipq *, u_int32_t *, u_int16_t *));
2078948e4baSArchie Cobbs #else
2086a800098SYoshinobu Inoue static struct	mbuf *ip_reass __P((struct mbuf *, struct ipq *, struct ipq *));
2098948e4baSArchie Cobbs #endif
2108948e4baSArchie Cobbs static struct	in_ifaddr *ip_rtaddr __P((struct in_addr));
2110312fbe9SPoul-Henning Kamp static void	ipintr __P((void));
2128948e4baSArchie Cobbs 
213df8bae1dSRodney W. Grimes /*
214df8bae1dSRodney W. Grimes  * IP initialization: fill in IP protocol switch table.
215df8bae1dSRodney W. Grimes  * All protocols not implemented in kernel go to raw IP protocol handler.
216df8bae1dSRodney W. Grimes  */
217df8bae1dSRodney W. Grimes void
218df8bae1dSRodney W. Grimes ip_init()
219df8bae1dSRodney W. Grimes {
2206a800098SYoshinobu Inoue 	register struct ipprotosw *pr;
221df8bae1dSRodney W. Grimes 	register int i;
222df8bae1dSRodney W. Grimes 
22359562606SGarrett Wollman 	TAILQ_INIT(&in_ifaddrhead);
2246a800098SYoshinobu Inoue 	pr = (struct ipprotosw *)pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW);
225df8bae1dSRodney W. Grimes 	if (pr == 0)
226df8bae1dSRodney W. Grimes 		panic("ip_init");
227df8bae1dSRodney W. Grimes 	for (i = 0; i < IPPROTO_MAX; i++)
228df8bae1dSRodney W. Grimes 		ip_protox[i] = pr - inetsw;
2296a800098SYoshinobu Inoue 	for (pr = (struct ipprotosw *)inetdomain.dom_protosw;
2306a800098SYoshinobu Inoue 	    pr < (struct ipprotosw *)inetdomain.dom_protoswNPROTOSW; pr++)
231df8bae1dSRodney W. Grimes 		if (pr->pr_domain->dom_family == PF_INET &&
232df8bae1dSRodney W. Grimes 		    pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
233df8bae1dSRodney W. Grimes 			ip_protox[pr->pr_protocol] = pr - inetsw;
234194a213eSAndrey A. Chernov 
235194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++)
236194a213eSAndrey A. Chernov 	    ipq[i].next = ipq[i].prev = &ipq[i];
237194a213eSAndrey A. Chernov 
238194a213eSAndrey A. Chernov 	maxnipq = nmbclusters/4;
239194a213eSAndrey A. Chernov 
240227ee8a1SPoul-Henning Kamp 	ip_id = time_second & 0xffff;
241df8bae1dSRodney W. Grimes 	ipintrq.ifq_maxlen = ipqmaxlen;
242df5e1987SJonathan Lemon 	mtx_init(&ipintrq.ifq_mtx, "ip_inq", MTX_DEF);
243242c5536SPeter Wemm 
244242c5536SPeter Wemm 	register_netisr(NETISR_IP, ipintr);
245df8bae1dSRodney W. Grimes }
246df8bae1dSRodney W. Grimes 
2470312fbe9SPoul-Henning Kamp static struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
248f708ef1bSPoul-Henning Kamp static struct	route ipforward_rt;
249df8bae1dSRodney W. Grimes 
250df8bae1dSRodney W. Grimes /*
251df8bae1dSRodney W. Grimes  * Ip input routine.  Checksum and byte swap header.  If fragmented
252df8bae1dSRodney W. Grimes  * try to reassemble.  Process options.  Pass to next level.
253df8bae1dSRodney W. Grimes  */
254c67b1d17SGarrett Wollman void
255c67b1d17SGarrett Wollman ip_input(struct mbuf *m)
256df8bae1dSRodney W. Grimes {
25723bf9953SPoul-Henning Kamp 	struct ip *ip;
25823bf9953SPoul-Henning Kamp 	struct ipq *fp;
2595da9f8faSJosef Karthauser 	struct in_ifaddr *ia = NULL;
260b6ea1aa5SRuslan Ermilov 	int    i, hlen;
26147c861ecSBrian Somers 	u_short sum;
2628948e4baSArchie Cobbs 	u_int16_t divert_cookie;		/* firewall cookie */
2637538a9a0SJonathan Lemon 	struct in_addr pkt_dst;
2648948e4baSArchie Cobbs #ifdef IPDIVERT
2658948e4baSArchie Cobbs 	u_int32_t divert_info = 0;		/* packet divert/tee info */
266b715f178SLuigi Rizzo #endif
267b715f178SLuigi Rizzo 	struct ip_fw_chain *rule = NULL;
268c4ac87eaSDarren Reed #ifdef PFIL_HOOKS
269c4ac87eaSDarren Reed 	struct packet_filter_hook *pfh;
270c4ac87eaSDarren Reed 	struct mbuf *m0;
271c4ac87eaSDarren Reed 	int rv;
272c4ac87eaSDarren Reed #endif /* PFIL_HOOKS */
273b715f178SLuigi Rizzo 
2748948e4baSArchie Cobbs #ifdef IPDIVERT
2758948e4baSArchie Cobbs 	/* Get and reset firewall cookie */
2768948e4baSArchie Cobbs 	divert_cookie = ip_divert_cookie;
2778948e4baSArchie Cobbs 	ip_divert_cookie = 0;
2788948e4baSArchie Cobbs #else
2798948e4baSArchie Cobbs 	divert_cookie = 0;
2808948e4baSArchie Cobbs #endif
2818948e4baSArchie Cobbs 
282b715f178SLuigi Rizzo #if defined(IPFIREWALL) && defined(DUMMYNET)
283b715f178SLuigi Rizzo         /*
284b715f178SLuigi Rizzo          * dummynet packet are prepended a vestigial mbuf with
285b715f178SLuigi Rizzo          * m_type = MT_DUMMYNET and m_data pointing to the matching
286b715f178SLuigi Rizzo          * rule.
287b715f178SLuigi Rizzo          */
288b715f178SLuigi Rizzo         if (m->m_type == MT_DUMMYNET) {
289b715f178SLuigi Rizzo             rule = (struct ip_fw_chain *)(m->m_data) ;
290b715f178SLuigi Rizzo             m = m->m_next ;
291b715f178SLuigi Rizzo             ip = mtod(m, struct ip *);
292b715f178SLuigi Rizzo             hlen = IP_VHL_HL(ip->ip_vhl) << 2;
293b715f178SLuigi Rizzo             goto iphack ;
294b715f178SLuigi Rizzo         } else
295b715f178SLuigi Rizzo             rule = NULL ;
296b715f178SLuigi Rizzo #endif
297df8bae1dSRodney W. Grimes 
298df8bae1dSRodney W. Grimes #ifdef	DIAGNOSTIC
299ed7509acSJulian Elischer 	if (m == NULL || (m->m_flags & M_PKTHDR) == 0)
30058938916SGarrett Wollman 		panic("ip_input no HDR");
301df8bae1dSRodney W. Grimes #endif
302df8bae1dSRodney W. Grimes 	ipstat.ips_total++;
30358938916SGarrett Wollman 
30458938916SGarrett Wollman 	if (m->m_pkthdr.len < sizeof(struct ip))
30558938916SGarrett Wollman 		goto tooshort;
30658938916SGarrett Wollman 
307df8bae1dSRodney W. Grimes 	if (m->m_len < sizeof (struct ip) &&
308df8bae1dSRodney W. Grimes 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
309df8bae1dSRodney W. Grimes 		ipstat.ips_toosmall++;
310c67b1d17SGarrett Wollman 		return;
311df8bae1dSRodney W. Grimes 	}
312df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
31358938916SGarrett Wollman 
31458938916SGarrett Wollman 	if (IP_VHL_V(ip->ip_vhl) != IPVERSION) {
315df8bae1dSRodney W. Grimes 		ipstat.ips_badvers++;
316df8bae1dSRodney W. Grimes 		goto bad;
317df8bae1dSRodney W. Grimes 	}
31858938916SGarrett Wollman 
31958938916SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
320df8bae1dSRodney W. Grimes 	if (hlen < sizeof(struct ip)) {	/* minimum header length */
321df8bae1dSRodney W. Grimes 		ipstat.ips_badhlen++;
322df8bae1dSRodney W. Grimes 		goto bad;
323df8bae1dSRodney W. Grimes 	}
324df8bae1dSRodney W. Grimes 	if (hlen > m->m_len) {
325df8bae1dSRodney W. Grimes 		if ((m = m_pullup(m, hlen)) == 0) {
326df8bae1dSRodney W. Grimes 			ipstat.ips_badhlen++;
327c67b1d17SGarrett Wollman 			return;
328df8bae1dSRodney W. Grimes 		}
329df8bae1dSRodney W. Grimes 		ip = mtod(m, struct ip *);
330df8bae1dSRodney W. Grimes 	}
331db4f9cc7SJonathan Lemon 	if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) {
332db4f9cc7SJonathan Lemon 		sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
333db4f9cc7SJonathan Lemon 	} else {
33458938916SGarrett Wollman 		if (hlen == sizeof(struct ip)) {
33547c861ecSBrian Somers 			sum = in_cksum_hdr(ip);
33658938916SGarrett Wollman 		} else {
33747c861ecSBrian Somers 			sum = in_cksum(m, hlen);
33858938916SGarrett Wollman 		}
339db4f9cc7SJonathan Lemon 	}
34047c861ecSBrian Somers 	if (sum) {
341df8bae1dSRodney W. Grimes 		ipstat.ips_badsum++;
342df8bae1dSRodney W. Grimes 		goto bad;
343df8bae1dSRodney W. Grimes 	}
344df8bae1dSRodney W. Grimes 
345df8bae1dSRodney W. Grimes 	/*
346df8bae1dSRodney W. Grimes 	 * Convert fields to host representation.
347df8bae1dSRodney W. Grimes 	 */
348df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_len);
349df8bae1dSRodney W. Grimes 	if (ip->ip_len < hlen) {
350df8bae1dSRodney W. Grimes 		ipstat.ips_badlen++;
351df8bae1dSRodney W. Grimes 		goto bad;
352df8bae1dSRodney W. Grimes 	}
353df8bae1dSRodney W. Grimes 	NTOHS(ip->ip_off);
354df8bae1dSRodney W. Grimes 
355df8bae1dSRodney W. Grimes 	/*
356df8bae1dSRodney W. Grimes 	 * Check that the amount of data in the buffers
357df8bae1dSRodney W. Grimes 	 * is as at least much as the IP header would have us expect.
358df8bae1dSRodney W. Grimes 	 * Trim mbufs if longer than we expect.
359df8bae1dSRodney W. Grimes 	 * Drop packet if shorter than we expect.
360df8bae1dSRodney W. Grimes 	 */
361df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len < ip->ip_len) {
36258938916SGarrett Wollman tooshort:
363df8bae1dSRodney W. Grimes 		ipstat.ips_tooshort++;
364df8bae1dSRodney W. Grimes 		goto bad;
365df8bae1dSRodney W. Grimes 	}
366df8bae1dSRodney W. Grimes 	if (m->m_pkthdr.len > ip->ip_len) {
367df8bae1dSRodney W. Grimes 		if (m->m_len == m->m_pkthdr.len) {
368df8bae1dSRodney W. Grimes 			m->m_len = ip->ip_len;
369df8bae1dSRodney W. Grimes 			m->m_pkthdr.len = ip->ip_len;
370df8bae1dSRodney W. Grimes 		} else
371df8bae1dSRodney W. Grimes 			m_adj(m, ip->ip_len - m->m_pkthdr.len);
372df8bae1dSRodney W. Grimes 	}
3734dd1662bSUgen J.S. Antsilevich 	/*
3744dd1662bSUgen J.S. Antsilevich 	 * IpHack's section.
3754dd1662bSUgen J.S. Antsilevich 	 * Right now when no processing on packet has done
3764dd1662bSUgen J.S. Antsilevich 	 * and it is still fresh out of network we do our black
3774dd1662bSUgen J.S. Antsilevich 	 * deals with it.
37893e0e116SJulian Elischer 	 * - Firewall: deny/allow/divert
379fed1c7e9SSøren Schmidt 	 * - Xlate: translate packet's addr/port (NAT).
380b715f178SLuigi Rizzo 	 * - Pipe: pass pkt through dummynet.
3814dd1662bSUgen J.S. Antsilevich 	 * - Wrap: fake packet's addr/port <unimpl.>
3824dd1662bSUgen J.S. Antsilevich 	 * - Encapsulate: put it in another IP and send out. <unimp.>
3834dd1662bSUgen J.S. Antsilevich  	 */
384b715f178SLuigi Rizzo 
385dee383e0SEivind Eklund #if defined(IPFIREWALL) && defined(DUMMYNET)
386b715f178SLuigi Rizzo iphack:
387dee383e0SEivind Eklund #endif
388df8bae1dSRodney W. Grimes 
389c4ac87eaSDarren Reed #ifdef PFIL_HOOKS
390c4ac87eaSDarren Reed 	/*
391c4ac87eaSDarren Reed 	 * Run through list of hooks for input packets.  If there are any
392c4ac87eaSDarren Reed 	 * filters which require that additional packets in the flow are
393c4ac87eaSDarren Reed 	 * not fast-forwarded, they must clear the M_CANFASTFWD flag.
394c4ac87eaSDarren Reed 	 * Note that filters must _never_ set this flag, as another filter
395c4ac87eaSDarren Reed 	 * in the list may have previously cleared it.
396c4ac87eaSDarren Reed 	 */
397c4ac87eaSDarren Reed 	m0 = m;
398c4ac87eaSDarren Reed 	pfh = pfil_hook_get(PFIL_IN, &inetsw[ip_protox[IPPROTO_IP]].pr_pfh);
399fc2ffbe6SPoul-Henning Kamp 	for (; pfh; pfh = TAILQ_NEXT(pfh, pfil_link))
400c4ac87eaSDarren Reed 		if (pfh->pfil_func) {
401c4ac87eaSDarren Reed 			rv = pfh->pfil_func(ip, hlen,
402c4ac87eaSDarren Reed 					    m->m_pkthdr.rcvif, 0, &m0);
403c4ac87eaSDarren Reed 			if (rv)
404beec8214SDarren Reed 				return;
405c4ac87eaSDarren Reed 			m = m0;
406c4ac87eaSDarren Reed 			if (m == NULL)
407c4ac87eaSDarren Reed 				return;
408c4ac87eaSDarren Reed 			ip = mtod(m, struct ip *);
409beec8214SDarren Reed 		}
410c4ac87eaSDarren Reed #endif /* PFIL_HOOKS */
411c4ac87eaSDarren Reed 
4126bc748b0SLuigi Rizzo 	if (fw_enable && ip_fw_chk_ptr) {
413f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
414f9e354dfSJulian Elischer 		/*
415f9e354dfSJulian Elischer 		 * If we've been forwarded from the output side, then
416f9e354dfSJulian Elischer 		 * skip the firewall a second time
417f9e354dfSJulian Elischer 		 */
418f9e354dfSJulian Elischer 		if (ip_fw_fwd_addr)
419f9e354dfSJulian Elischer 			goto ours;
420f9e354dfSJulian Elischer #endif	/* IPFIREWALL_FORWARD */
421f9e354dfSJulian Elischer 		/*
4228948e4baSArchie Cobbs 		 * See the comment in ip_output for the return values
423b715f178SLuigi Rizzo 		 * produced by the firewall.
424f9e354dfSJulian Elischer 		 */
4258948e4baSArchie Cobbs 		i = (*ip_fw_chk_ptr)(&ip,
4268948e4baSArchie Cobbs 		    hlen, NULL, &divert_cookie, &m, &rule, &ip_fw_fwd_addr);
427507b4b54SLuigi Rizzo 		if (i & IP_FW_PORT_DENY_FLAG) { /* XXX new interface-denied */
428507b4b54SLuigi Rizzo 		    if (m)
429507b4b54SLuigi Rizzo 			m_freem(m);
430b715f178SLuigi Rizzo 		    return ;
431507b4b54SLuigi Rizzo 		}
432507b4b54SLuigi Rizzo 		if (m == NULL) {	/* Packet discarded by firewall */
433507b4b54SLuigi Rizzo 		    static int __debug=10;
434507b4b54SLuigi Rizzo 		    if (__debug >0) {
435507b4b54SLuigi Rizzo 			printf("firewall returns NULL, please update!\n");
436507b4b54SLuigi Rizzo 			__debug-- ;
437507b4b54SLuigi Rizzo 		    }
438507b4b54SLuigi Rizzo 		    return;
439507b4b54SLuigi Rizzo 		}
440b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr == NULL)	/* common case */
441b715f178SLuigi Rizzo 			goto pass;
442b715f178SLuigi Rizzo #ifdef DUMMYNET
4438948e4baSArchie Cobbs                 if ((i & IP_FW_PORT_DYNT_FLAG) != 0) {
4448948e4baSArchie Cobbs                         /* Send packet to the appropriate pipe */
4456a800098SYoshinobu Inoue                         dummynet_io(i&0xffff,DN_TO_IP_IN,m,NULL,NULL,0, rule,
4466a800098SYoshinobu Inoue 				    0);
447e4676ba6SJulian Elischer 			return;
44893e0e116SJulian Elischer 		}
449b715f178SLuigi Rizzo #endif
450b715f178SLuigi Rizzo #ifdef IPDIVERT
4518948e4baSArchie Cobbs 		if (i != 0 && (i & IP_FW_PORT_DYNT_FLAG) == 0) {
4528948e4baSArchie Cobbs 			/* Divert or tee packet */
4538948e4baSArchie Cobbs 			divert_info = i;
454b715f178SLuigi Rizzo 			goto ours;
455b715f178SLuigi Rizzo 		}
456b715f178SLuigi Rizzo #endif
457b715f178SLuigi Rizzo #ifdef IPFIREWALL_FORWARD
458b715f178SLuigi Rizzo 		if (i == 0 && ip_fw_fwd_addr != NULL)
459b715f178SLuigi Rizzo 			goto pass;
460b715f178SLuigi Rizzo #endif
461b715f178SLuigi Rizzo 		/*
462b715f178SLuigi Rizzo 		 * if we get here, the packet must be dropped
463b715f178SLuigi Rizzo 		 */
464b715f178SLuigi Rizzo 		m_freem(m);
465b715f178SLuigi Rizzo 		return;
466b715f178SLuigi Rizzo 	}
467b715f178SLuigi Rizzo pass:
468100ba1a6SJordan K. Hubbard 
469df8bae1dSRodney W. Grimes 	/*
470df8bae1dSRodney W. Grimes 	 * Process options and, if not destined for us,
471df8bae1dSRodney W. Grimes 	 * ship it on.  ip_dooptions returns 1 when an
472df8bae1dSRodney W. Grimes 	 * error was detected (causing an icmp message
473df8bae1dSRodney W. Grimes 	 * to be sent and the original packet to be freed).
474df8bae1dSRodney W. Grimes 	 */
475df8bae1dSRodney W. Grimes 	ip_nhops = 0;		/* for source routed packets */
476ed1ff184SJulian Elischer 	if (hlen > sizeof (struct ip) && ip_dooptions(m)) {
477ed1ff184SJulian Elischer #ifdef IPFIREWALL_FORWARD
478ed1ff184SJulian Elischer 		ip_fw_fwd_addr = NULL;
479ed1ff184SJulian Elischer #endif
480c67b1d17SGarrett Wollman 		return;
481ed1ff184SJulian Elischer 	}
482df8bae1dSRodney W. Grimes 
483f0068c4aSGarrett Wollman         /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no
484f0068c4aSGarrett Wollman          * matter if it is destined to another node, or whether it is
485f0068c4aSGarrett Wollman          * a multicast one, RSVP wants it! and prevents it from being forwarded
486f0068c4aSGarrett Wollman          * anywhere else. Also checks if the rsvp daemon is running before
487f0068c4aSGarrett Wollman 	 * grabbing the packet.
488f0068c4aSGarrett Wollman          */
4891c5de19aSGarrett Wollman 	if (rsvp_on && ip->ip_p==IPPROTO_RSVP)
490f0068c4aSGarrett Wollman 		goto ours;
491f0068c4aSGarrett Wollman 
492df8bae1dSRodney W. Grimes 	/*
493df8bae1dSRodney W. Grimes 	 * Check our list of addresses, to see if the packet is for us.
494cc766e04SGarrett Wollman 	 * If we don't have any addresses, assume any unicast packet
495cc766e04SGarrett Wollman 	 * we receive might be for us (and let the upper layers deal
496cc766e04SGarrett Wollman 	 * with it).
497df8bae1dSRodney W. Grimes 	 */
498cc766e04SGarrett Wollman 	if (TAILQ_EMPTY(&in_ifaddrhead) &&
499cc766e04SGarrett Wollman 	    (m->m_flags & (M_MCAST|M_BCAST)) == 0)
500cc766e04SGarrett Wollman 		goto ours;
501cc766e04SGarrett Wollman 
5027538a9a0SJonathan Lemon 	/*
5037538a9a0SJonathan Lemon 	 * Cache the destination address of the packet; this may be
5047538a9a0SJonathan Lemon 	 * changed by use of 'ipfw fwd'.
5057538a9a0SJonathan Lemon 	 */
5067538a9a0SJonathan Lemon 	pkt_dst = ip_fw_fwd_addr == NULL ?
5077538a9a0SJonathan Lemon 	    ip->ip_dst : ip_fw_fwd_addr->sin_addr;
5087538a9a0SJonathan Lemon 
50937d40066SPoul-Henning Kamp 	TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
510df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
511df8bae1dSRodney W. Grimes 
512432aad0eSTor Egge #ifdef BOOTP_COMPAT
513432aad0eSTor Egge 		if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
514432aad0eSTor Egge 			goto ours;
515432aad0eSTor Egge #endif
516f9e354dfSJulian Elischer 		/*
5177538a9a0SJonathan Lemon 		 * check that the packet is either arriving from the
5187538a9a0SJonathan Lemon 		 * correct interface or is locally generated.
519f9e354dfSJulian Elischer 		 */
520b3e95d4eSJonathan Lemon 		if (ia->ia_ifp != m->m_pkthdr.rcvif && ip_checkinterface &&
5217538a9a0SJonathan Lemon 		     (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0)
5227538a9a0SJonathan Lemon 			continue;
5237538a9a0SJonathan Lemon 
5247538a9a0SJonathan Lemon 		if (IA_SIN(ia)->sin_addr.s_addr == pkt_dst.s_addr)
525ed1ff184SJulian Elischer 			goto ours;
5267538a9a0SJonathan Lemon 
5276ed666afSPoul-Henning Kamp 		if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
528df8bae1dSRodney W. Grimes 			if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
5297538a9a0SJonathan Lemon 			    pkt_dst.s_addr)
530df8bae1dSRodney W. Grimes 				goto ours;
5317538a9a0SJonathan Lemon 			if (ia->ia_netbroadcast.s_addr == pkt_dst.s_addr)
532df8bae1dSRodney W. Grimes 				goto ours;
533df8bae1dSRodney W. Grimes 		}
534df8bae1dSRodney W. Grimes 	}
535df8bae1dSRodney W. Grimes 	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
536df8bae1dSRodney W. Grimes 		struct in_multi *inm;
537df8bae1dSRodney W. Grimes 		if (ip_mrouter) {
538df8bae1dSRodney W. Grimes 			/*
539df8bae1dSRodney W. Grimes 			 * If we are acting as a multicast router, all
540df8bae1dSRodney W. Grimes 			 * incoming multicast packets are passed to the
541df8bae1dSRodney W. Grimes 			 * kernel-level multicast forwarding function.
542df8bae1dSRodney W. Grimes 			 * The packet is returned (relatively) intact; if
543df8bae1dSRodney W. Grimes 			 * ip_mforward() returns a non-zero value, the packet
544df8bae1dSRodney W. Grimes 			 * must be discarded, else it may be accepted below.
545df8bae1dSRodney W. Grimes 			 */
546f0068c4aSGarrett Wollman 			if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) {
547df8bae1dSRodney W. Grimes 				ipstat.ips_cantforward++;
548df8bae1dSRodney W. Grimes 				m_freem(m);
549c67b1d17SGarrett Wollman 				return;
550df8bae1dSRodney W. Grimes 			}
551df8bae1dSRodney W. Grimes 
552df8bae1dSRodney W. Grimes 			/*
553df8bae1dSRodney W. Grimes 			 * The process-level routing demon needs to receive
554df8bae1dSRodney W. Grimes 			 * all multicast IGMP packets, whether or not this
555df8bae1dSRodney W. Grimes 			 * host belongs to their destination groups.
556df8bae1dSRodney W. Grimes 			 */
557df8bae1dSRodney W. Grimes 			if (ip->ip_p == IPPROTO_IGMP)
558df8bae1dSRodney W. Grimes 				goto ours;
559df8bae1dSRodney W. Grimes 			ipstat.ips_forward++;
560df8bae1dSRodney W. Grimes 		}
561df8bae1dSRodney W. Grimes 		/*
562df8bae1dSRodney W. Grimes 		 * See if we belong to the destination multicast group on the
563df8bae1dSRodney W. Grimes 		 * arrival interface.
564df8bae1dSRodney W. Grimes 		 */
565df8bae1dSRodney W. Grimes 		IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm);
566df8bae1dSRodney W. Grimes 		if (inm == NULL) {
56782c39223SGarrett Wollman 			ipstat.ips_notmember++;
568df8bae1dSRodney W. Grimes 			m_freem(m);
569c67b1d17SGarrett Wollman 			return;
570df8bae1dSRodney W. Grimes 		}
571df8bae1dSRodney W. Grimes 		goto ours;
572df8bae1dSRodney W. Grimes 	}
573df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST)
574df8bae1dSRodney W. Grimes 		goto ours;
575df8bae1dSRodney W. Grimes 	if (ip->ip_dst.s_addr == INADDR_ANY)
576df8bae1dSRodney W. Grimes 		goto ours;
577df8bae1dSRodney W. Grimes 
5786a800098SYoshinobu Inoue #if defined(NFAITH) && 0 < NFAITH
5796a800098SYoshinobu Inoue 	/*
5806a800098SYoshinobu Inoue 	 * FAITH(Firewall Aided Internet Translator)
5816a800098SYoshinobu Inoue 	 */
5826a800098SYoshinobu Inoue 	if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
5836a800098SYoshinobu Inoue 		if (ip_keepfaith) {
5846a800098SYoshinobu Inoue 			if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_ICMP)
5856a800098SYoshinobu Inoue 				goto ours;
5866a800098SYoshinobu Inoue 		}
5876a800098SYoshinobu Inoue 		m_freem(m);
5886a800098SYoshinobu Inoue 		return;
5896a800098SYoshinobu Inoue 	}
5906a800098SYoshinobu Inoue #endif
591df8bae1dSRodney W. Grimes 	/*
592df8bae1dSRodney W. Grimes 	 * Not for us; forward if possible and desirable.
593df8bae1dSRodney W. Grimes 	 */
594df8bae1dSRodney W. Grimes 	if (ipforwarding == 0) {
595df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
596df8bae1dSRodney W. Grimes 		m_freem(m);
597df8bae1dSRodney W. Grimes 	} else
598df8bae1dSRodney W. Grimes 		ip_forward(m, 0);
599ed1ff184SJulian Elischer #ifdef IPFIREWALL_FORWARD
600ed1ff184SJulian Elischer 	ip_fw_fwd_addr = NULL;
601ed1ff184SJulian Elischer #endif
602c67b1d17SGarrett Wollman 	return;
603df8bae1dSRodney W. Grimes 
604df8bae1dSRodney W. Grimes ours:
6055da9f8faSJosef Karthauser 	/* Count the packet in the ip address stats */
6065da9f8faSJosef Karthauser 	if (ia != NULL) {
6075da9f8faSJosef Karthauser 		ia->ia_ifa.if_ipackets++;
6085da9f8faSJosef Karthauser 		ia->ia_ifa.if_ibytes += m->m_pkthdr.len;
6095da9f8faSJosef Karthauser 	}
610100ba1a6SJordan K. Hubbard 
61163f8d699SJordan K. Hubbard 	/*
612df8bae1dSRodney W. Grimes 	 * If offset or IP_MF are set, must reassemble.
613df8bae1dSRodney W. Grimes 	 * Otherwise, nothing need be done.
614df8bae1dSRodney W. Grimes 	 * (We could look in the reassembly queue to see
615df8bae1dSRodney W. Grimes 	 * if the packet was previously fragmented,
616df8bae1dSRodney W. Grimes 	 * but it's not worth the time; just let them time out.)
617df8bae1dSRodney W. Grimes 	 */
618b6ea1aa5SRuslan Ermilov 	if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
6196a800098SYoshinobu Inoue 
620194a213eSAndrey A. Chernov 		sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
621df8bae1dSRodney W. Grimes 		/*
622df8bae1dSRodney W. Grimes 		 * Look for queue of fragments
623df8bae1dSRodney W. Grimes 		 * of this datagram.
624df8bae1dSRodney W. Grimes 		 */
625194a213eSAndrey A. Chernov 		for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
626df8bae1dSRodney W. Grimes 			if (ip->ip_id == fp->ipq_id &&
627df8bae1dSRodney W. Grimes 			    ip->ip_src.s_addr == fp->ipq_src.s_addr &&
628df8bae1dSRodney W. Grimes 			    ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
629df8bae1dSRodney W. Grimes 			    ip->ip_p == fp->ipq_p)
630df8bae1dSRodney W. Grimes 				goto found;
631df8bae1dSRodney W. Grimes 
632194a213eSAndrey A. Chernov 		fp = 0;
633194a213eSAndrey A. Chernov 
634194a213eSAndrey A. Chernov 		/* check if there's a place for the new queue */
635194a213eSAndrey A. Chernov 		if (nipq > maxnipq) {
636194a213eSAndrey A. Chernov 		    /*
637194a213eSAndrey A. Chernov 		     * drop something from the tail of the current queue
638194a213eSAndrey A. Chernov 		     * before proceeding further
639194a213eSAndrey A. Chernov 		     */
640194a213eSAndrey A. Chernov 		    if (ipq[sum].prev == &ipq[sum]) {   /* gak */
641194a213eSAndrey A. Chernov 			for (i = 0; i < IPREASS_NHASH; i++) {
642194a213eSAndrey A. Chernov 			    if (ipq[i].prev != &ipq[i]) {
643194a213eSAndrey A. Chernov 				ip_freef(ipq[i].prev);
644194a213eSAndrey A. Chernov 				break;
645194a213eSAndrey A. Chernov 			    }
646194a213eSAndrey A. Chernov 			}
647194a213eSAndrey A. Chernov 		    } else
648194a213eSAndrey A. Chernov 			ip_freef(ipq[sum].prev);
649194a213eSAndrey A. Chernov 		}
650194a213eSAndrey A. Chernov found:
651df8bae1dSRodney W. Grimes 		/*
652df8bae1dSRodney W. Grimes 		 * Adjust ip_len to not reflect header,
653df8bae1dSRodney W. Grimes 		 * convert offset of this to bytes.
654df8bae1dSRodney W. Grimes 		 */
655df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
656b6ea1aa5SRuslan Ermilov 		if (ip->ip_off & IP_MF) {
6576effc713SDoug Rabson 		        /*
6586effc713SDoug Rabson 		         * Make sure that fragments have a data length
6596effc713SDoug Rabson 			 * that's a non-zero multiple of 8 bytes.
6606effc713SDoug Rabson 		         */
6616effc713SDoug Rabson 			if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
6626effc713SDoug Rabson 				ipstat.ips_toosmall++; /* XXX */
6636effc713SDoug Rabson 				goto bad;
6646effc713SDoug Rabson 			}
6656effc713SDoug Rabson 			m->m_flags |= M_FRAG;
6666effc713SDoug Rabson 		}
667df8bae1dSRodney W. Grimes 		ip->ip_off <<= 3;
668df8bae1dSRodney W. Grimes 
669df8bae1dSRodney W. Grimes 		/*
670b6ea1aa5SRuslan Ermilov 		 * Attempt reassembly; if it succeeds, proceed.
671df8bae1dSRodney W. Grimes 		 */
672df8bae1dSRodney W. Grimes 		ipstat.ips_fragments++;
673487bdb38SRuslan Ermilov 		m->m_pkthdr.header = ip;
6748948e4baSArchie Cobbs #ifdef IPDIVERT
6756a800098SYoshinobu Inoue 		m = ip_reass(m,
6768948e4baSArchie Cobbs 		    fp, &ipq[sum], &divert_info, &divert_cookie);
6778948e4baSArchie Cobbs #else
6786a800098SYoshinobu Inoue 		m = ip_reass(m, fp, &ipq[sum]);
6798948e4baSArchie Cobbs #endif
6806a800098SYoshinobu Inoue 		if (m == 0) {
681f9e354dfSJulian Elischer #ifdef IPFIREWALL_FORWARD
682f9e354dfSJulian Elischer 			ip_fw_fwd_addr = NULL;
683f9e354dfSJulian Elischer #endif
684c67b1d17SGarrett Wollman 			return;
685f9e354dfSJulian Elischer 		}
686df8bae1dSRodney W. Grimes 		ipstat.ips_reassembled++;
6876a800098SYoshinobu Inoue 		ip = mtod(m, struct ip *);
6887e2df452SRuslan Ermilov 		/* Get the header length of the reassembled packet */
6897e2df452SRuslan Ermilov 		hlen = IP_VHL_HL(ip->ip_vhl) << 2;
690af782f1cSBrian Somers #ifdef IPDIVERT
6918948e4baSArchie Cobbs 		/* Restore original checksum before diverting packet */
6928948e4baSArchie Cobbs 		if (divert_info != 0) {
693af782f1cSBrian Somers 			ip->ip_len += hlen;
694af782f1cSBrian Somers 			HTONS(ip->ip_len);
695af782f1cSBrian Somers 			HTONS(ip->ip_off);
696af782f1cSBrian Somers 			ip->ip_sum = 0;
69760123168SRuslan Ermilov 			if (hlen == sizeof(struct ip))
698af782f1cSBrian Somers 				ip->ip_sum = in_cksum_hdr(ip);
69960123168SRuslan Ermilov 			else
70060123168SRuslan Ermilov 				ip->ip_sum = in_cksum(m, hlen);
701af782f1cSBrian Somers 			NTOHS(ip->ip_off);
702af782f1cSBrian Somers 			NTOHS(ip->ip_len);
703af782f1cSBrian Somers 			ip->ip_len -= hlen;
704af782f1cSBrian Somers 		}
705af782f1cSBrian Somers #endif
706df8bae1dSRodney W. Grimes 	} else
707df8bae1dSRodney W. Grimes 		ip->ip_len -= hlen;
708df8bae1dSRodney W. Grimes 
70993e0e116SJulian Elischer #ifdef IPDIVERT
71093e0e116SJulian Elischer 	/*
7118948e4baSArchie Cobbs 	 * Divert or tee packet to the divert protocol if required.
7128948e4baSArchie Cobbs 	 *
7138948e4baSArchie Cobbs 	 * If divert_info is zero then cookie should be too, so we shouldn't
7148948e4baSArchie Cobbs 	 * need to clear them here.  Assume divert_packet() does so also.
71593e0e116SJulian Elischer 	 */
7168948e4baSArchie Cobbs 	if (divert_info != 0) {
7178948e4baSArchie Cobbs 		struct mbuf *clone = NULL;
7188948e4baSArchie Cobbs 
7198948e4baSArchie Cobbs 		/* Clone packet if we're doing a 'tee' */
7208948e4baSArchie Cobbs 		if ((divert_info & IP_FW_PORT_TEE_FLAG) != 0)
7218948e4baSArchie Cobbs 			clone = m_dup(m, M_DONTWAIT);
7228948e4baSArchie Cobbs 
7238948e4baSArchie Cobbs 		/* Restore packet header fields to original values */
7248948e4baSArchie Cobbs 		ip->ip_len += hlen;
7258948e4baSArchie Cobbs 		HTONS(ip->ip_len);
7268948e4baSArchie Cobbs 		HTONS(ip->ip_off);
7278948e4baSArchie Cobbs 
7288948e4baSArchie Cobbs 		/* Deliver packet to divert input routine */
7298948e4baSArchie Cobbs 		ip_divert_cookie = divert_cookie;
7308948e4baSArchie Cobbs 		divert_packet(m, 1, divert_info & 0xffff);
731e4676ba6SJulian Elischer 		ipstat.ips_delivered++;
7328948e4baSArchie Cobbs 
7338948e4baSArchie Cobbs 		/* If 'tee', continue with original packet */
7348948e4baSArchie Cobbs 		if (clone == NULL)
73593e0e116SJulian Elischer 			return;
7368948e4baSArchie Cobbs 		m = clone;
7378948e4baSArchie Cobbs 		ip = mtod(m, struct ip *);
73893e0e116SJulian Elischer 	}
73993e0e116SJulian Elischer #endif
74093e0e116SJulian Elischer 
741df8bae1dSRodney W. Grimes 	/*
742df8bae1dSRodney W. Grimes 	 * Switch out to protocol's input routine.
743df8bae1dSRodney W. Grimes 	 */
744df8bae1dSRodney W. Grimes 	ipstat.ips_delivered++;
7456a800098SYoshinobu Inoue     {
7466a800098SYoshinobu Inoue 	int off = hlen, nh = ip->ip_p;
7476a800098SYoshinobu Inoue 
7486a800098SYoshinobu Inoue 	(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, off, nh);
749f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
750f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;	/* tcp needed it */
751f9e354dfSJulian Elischer #endif
752c67b1d17SGarrett Wollman 	return;
7536a800098SYoshinobu Inoue     }
754df8bae1dSRodney W. Grimes bad:
755f9e354dfSJulian Elischer #ifdef	IPFIREWALL_FORWARD
756f9e354dfSJulian Elischer 	ip_fw_fwd_addr = NULL;
757f9e354dfSJulian Elischer #endif
758df8bae1dSRodney W. Grimes 	m_freem(m);
759c67b1d17SGarrett Wollman }
760c67b1d17SGarrett Wollman 
761c67b1d17SGarrett Wollman /*
762c67b1d17SGarrett Wollman  * IP software interrupt routine - to go away sometime soon
763c67b1d17SGarrett Wollman  */
764c67b1d17SGarrett Wollman static void
765c67b1d17SGarrett Wollman ipintr(void)
766c67b1d17SGarrett Wollman {
767c67b1d17SGarrett Wollman 	struct mbuf *m;
768c67b1d17SGarrett Wollman 
769c67b1d17SGarrett Wollman 	while (1) {
770c67b1d17SGarrett Wollman 		IF_DEQUEUE(&ipintrq, m);
771c67b1d17SGarrett Wollman 		if (m == 0)
772c67b1d17SGarrett Wollman 			return;
773c67b1d17SGarrett Wollman 		ip_input(m);
774c67b1d17SGarrett Wollman 	}
775df8bae1dSRodney W. Grimes }
776df8bae1dSRodney W. Grimes 
777df8bae1dSRodney W. Grimes /*
7788948e4baSArchie Cobbs  * Take incoming datagram fragment and try to reassemble it into
7798948e4baSArchie Cobbs  * whole datagram.  If a chain for reassembly of this datagram already
7808948e4baSArchie Cobbs  * exists, then it is given as fp; otherwise have to make a chain.
7818948e4baSArchie Cobbs  *
7828948e4baSArchie Cobbs  * When IPDIVERT enabled, keep additional state with each packet that
7838948e4baSArchie Cobbs  * tells us if we need to divert or tee the packet we're building.
784df8bae1dSRodney W. Grimes  */
7858948e4baSArchie Cobbs 
7866a800098SYoshinobu Inoue static struct mbuf *
7878948e4baSArchie Cobbs #ifdef IPDIVERT
7888948e4baSArchie Cobbs ip_reass(m, fp, where, divinfo, divcookie)
7898948e4baSArchie Cobbs #else
7906effc713SDoug Rabson ip_reass(m, fp, where)
7918948e4baSArchie Cobbs #endif
7926effc713SDoug Rabson 	register struct mbuf *m;
793df8bae1dSRodney W. Grimes 	register struct ipq *fp;
794194a213eSAndrey A. Chernov 	struct   ipq    *where;
7958948e4baSArchie Cobbs #ifdef IPDIVERT
7968948e4baSArchie Cobbs 	u_int32_t *divinfo;
7978948e4baSArchie Cobbs 	u_int16_t *divcookie;
7988948e4baSArchie Cobbs #endif
799df8bae1dSRodney W. Grimes {
8006effc713SDoug Rabson 	struct ip *ip = mtod(m, struct ip *);
801b6ea1aa5SRuslan Ermilov 	register struct mbuf *p, *q, *nq;
802df8bae1dSRodney W. Grimes 	struct mbuf *t;
8036effc713SDoug Rabson 	int hlen = IP_VHL_HL(ip->ip_vhl) << 2;
804df8bae1dSRodney W. Grimes 	int i, next;
805df8bae1dSRodney W. Grimes 
806df8bae1dSRodney W. Grimes 	/*
807df8bae1dSRodney W. Grimes 	 * Presence of header sizes in mbufs
808df8bae1dSRodney W. Grimes 	 * would confuse code below.
809df8bae1dSRodney W. Grimes 	 */
810df8bae1dSRodney W. Grimes 	m->m_data += hlen;
811df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
812df8bae1dSRodney W. Grimes 
813df8bae1dSRodney W. Grimes 	/*
814df8bae1dSRodney W. Grimes 	 * If first fragment to arrive, create a reassembly queue.
815df8bae1dSRodney W. Grimes 	 */
816df8bae1dSRodney W. Grimes 	if (fp == 0) {
817df8bae1dSRodney W. Grimes 		if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
818df8bae1dSRodney W. Grimes 			goto dropfrag;
819df8bae1dSRodney W. Grimes 		fp = mtod(t, struct ipq *);
820194a213eSAndrey A. Chernov 		insque(fp, where);
821194a213eSAndrey A. Chernov 		nipq++;
822df8bae1dSRodney W. Grimes 		fp->ipq_ttl = IPFRAGTTL;
823df8bae1dSRodney W. Grimes 		fp->ipq_p = ip->ip_p;
824df8bae1dSRodney W. Grimes 		fp->ipq_id = ip->ip_id;
8256effc713SDoug Rabson 		fp->ipq_src = ip->ip_src;
8266effc713SDoug Rabson 		fp->ipq_dst = ip->ip_dst;
827af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
828af38c68cSLuigi Rizzo 		m->m_nextpkt = NULL;
82993e0e116SJulian Elischer #ifdef IPDIVERT
8308948e4baSArchie Cobbs 		fp->ipq_div_info = 0;
831bb60f459SJulian Elischer 		fp->ipq_div_cookie = 0;
83293e0e116SJulian Elischer #endif
833af38c68cSLuigi Rizzo 		goto inserted;
834df8bae1dSRodney W. Grimes 	}
835df8bae1dSRodney W. Grimes 
8366effc713SDoug Rabson #define GETIP(m)	((struct ip*)((m)->m_pkthdr.header))
8376effc713SDoug Rabson 
838df8bae1dSRodney W. Grimes 	/*
839df8bae1dSRodney W. Grimes 	 * Find a segment which begins after this one does.
840df8bae1dSRodney W. Grimes 	 */
8416effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
8426effc713SDoug Rabson 		if (GETIP(q)->ip_off > ip->ip_off)
843df8bae1dSRodney W. Grimes 			break;
844df8bae1dSRodney W. Grimes 
845df8bae1dSRodney W. Grimes 	/*
846df8bae1dSRodney W. Grimes 	 * If there is a preceding segment, it may provide some of
847df8bae1dSRodney W. Grimes 	 * our data already.  If so, drop the data from the incoming
848af38c68cSLuigi Rizzo 	 * segment.  If it provides all of our data, drop us, otherwise
849af38c68cSLuigi Rizzo 	 * stick new segment in the proper place.
850db4f9cc7SJonathan Lemon 	 *
851db4f9cc7SJonathan Lemon 	 * If some of the data is dropped from the the preceding
852db4f9cc7SJonathan Lemon 	 * segment, then it's checksum is invalidated.
853df8bae1dSRodney W. Grimes 	 */
8546effc713SDoug Rabson 	if (p) {
8556effc713SDoug Rabson 		i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;
856df8bae1dSRodney W. Grimes 		if (i > 0) {
857df8bae1dSRodney W. Grimes 			if (i >= ip->ip_len)
858df8bae1dSRodney W. Grimes 				goto dropfrag;
8596a800098SYoshinobu Inoue 			m_adj(m, i);
860db4f9cc7SJonathan Lemon 			m->m_pkthdr.csum_flags = 0;
861df8bae1dSRodney W. Grimes 			ip->ip_off += i;
862df8bae1dSRodney W. Grimes 			ip->ip_len -= i;
863df8bae1dSRodney W. Grimes 		}
864af38c68cSLuigi Rizzo 		m->m_nextpkt = p->m_nextpkt;
865af38c68cSLuigi Rizzo 		p->m_nextpkt = m;
866af38c68cSLuigi Rizzo 	} else {
867af38c68cSLuigi Rizzo 		m->m_nextpkt = fp->ipq_frags;
868af38c68cSLuigi Rizzo 		fp->ipq_frags = m;
869df8bae1dSRodney W. Grimes 	}
870df8bae1dSRodney W. Grimes 
871df8bae1dSRodney W. Grimes 	/*
872df8bae1dSRodney W. Grimes 	 * While we overlap succeeding segments trim them or,
873df8bae1dSRodney W. Grimes 	 * if they are completely covered, dequeue them.
874df8bae1dSRodney W. Grimes 	 */
8756effc713SDoug Rabson 	for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;
876af38c68cSLuigi Rizzo 	     q = nq) {
8776effc713SDoug Rabson 		i = (ip->ip_off + ip->ip_len) -
8786effc713SDoug Rabson 		    GETIP(q)->ip_off;
8796effc713SDoug Rabson 		if (i < GETIP(q)->ip_len) {
8806effc713SDoug Rabson 			GETIP(q)->ip_len -= i;
8816effc713SDoug Rabson 			GETIP(q)->ip_off += i;
8826effc713SDoug Rabson 			m_adj(q, i);
883db4f9cc7SJonathan Lemon 			q->m_pkthdr.csum_flags = 0;
884df8bae1dSRodney W. Grimes 			break;
885df8bae1dSRodney W. Grimes 		}
8866effc713SDoug Rabson 		nq = q->m_nextpkt;
887af38c68cSLuigi Rizzo 		m->m_nextpkt = nq;
8886effc713SDoug Rabson 		m_freem(q);
889df8bae1dSRodney W. Grimes 	}
890df8bae1dSRodney W. Grimes 
891af38c68cSLuigi Rizzo inserted:
89293e0e116SJulian Elischer 
89393e0e116SJulian Elischer #ifdef IPDIVERT
89493e0e116SJulian Elischer 	/*
8958948e4baSArchie Cobbs 	 * Transfer firewall instructions to the fragment structure.
8968948e4baSArchie Cobbs 	 * Any fragment diverting causes the whole packet to divert.
89793e0e116SJulian Elischer 	 */
8988948e4baSArchie Cobbs 	fp->ipq_div_info = *divinfo;
8998948e4baSArchie Cobbs 	fp->ipq_div_cookie = *divcookie;
9008948e4baSArchie Cobbs 	*divinfo = 0;
9018948e4baSArchie Cobbs 	*divcookie = 0;
90293e0e116SJulian Elischer #endif
90393e0e116SJulian Elischer 
904df8bae1dSRodney W. Grimes 	/*
905af38c68cSLuigi Rizzo 	 * Check for complete reassembly.
906df8bae1dSRodney W. Grimes 	 */
9076effc713SDoug Rabson 	next = 0;
9086effc713SDoug Rabson 	for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) {
9096effc713SDoug Rabson 		if (GETIP(q)->ip_off != next)
9106effc713SDoug Rabson 			return (0);
9116effc713SDoug Rabson 		next += GETIP(q)->ip_len;
9126effc713SDoug Rabson 	}
9136effc713SDoug Rabson 	/* Make sure the last packet didn't have the IP_MF flag */
9146effc713SDoug Rabson 	if (p->m_flags & M_FRAG)
915df8bae1dSRodney W. Grimes 		return (0);
916df8bae1dSRodney W. Grimes 
917df8bae1dSRodney W. Grimes 	/*
918430d30d8SBill Fenner 	 * Reassembly is complete.  Make sure the packet is a sane size.
919430d30d8SBill Fenner 	 */
9206effc713SDoug Rabson 	q = fp->ipq_frags;
9216effc713SDoug Rabson 	ip = GETIP(q);
9226effc713SDoug Rabson 	if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) {
923430d30d8SBill Fenner 		ipstat.ips_toolong++;
924430d30d8SBill Fenner 		ip_freef(fp);
925430d30d8SBill Fenner 		return (0);
926430d30d8SBill Fenner 	}
927430d30d8SBill Fenner 
928430d30d8SBill Fenner 	/*
929430d30d8SBill Fenner 	 * Concatenate fragments.
930df8bae1dSRodney W. Grimes 	 */
9316effc713SDoug Rabson 	m = q;
932df8bae1dSRodney W. Grimes 	t = m->m_next;
933df8bae1dSRodney W. Grimes 	m->m_next = 0;
934df8bae1dSRodney W. Grimes 	m_cat(m, t);
9356effc713SDoug Rabson 	nq = q->m_nextpkt;
936945aa40dSDoug Rabson 	q->m_nextpkt = 0;
9376effc713SDoug Rabson 	for (q = nq; q != NULL; q = nq) {
9386effc713SDoug Rabson 		nq = q->m_nextpkt;
939945aa40dSDoug Rabson 		q->m_nextpkt = NULL;
940db4f9cc7SJonathan Lemon 		m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags;
941db4f9cc7SJonathan Lemon 		m->m_pkthdr.csum_data += q->m_pkthdr.csum_data;
942a8db1d93SJonathan Lemon 		m_cat(m, q);
943df8bae1dSRodney W. Grimes 	}
944df8bae1dSRodney W. Grimes 
94593e0e116SJulian Elischer #ifdef IPDIVERT
94693e0e116SJulian Elischer 	/*
9478948e4baSArchie Cobbs 	 * Extract firewall instructions from the fragment structure.
94893e0e116SJulian Elischer 	 */
9498948e4baSArchie Cobbs 	*divinfo = fp->ipq_div_info;
9508948e4baSArchie Cobbs 	*divcookie = fp->ipq_div_cookie;
95193e0e116SJulian Elischer #endif
95293e0e116SJulian Elischer 
953df8bae1dSRodney W. Grimes 	/*
954df8bae1dSRodney W. Grimes 	 * Create header for new ip packet by
955df8bae1dSRodney W. Grimes 	 * modifying header of first packet;
956df8bae1dSRodney W. Grimes 	 * dequeue and discard fragment reassembly header.
957df8bae1dSRodney W. Grimes 	 * Make header visible.
958df8bae1dSRodney W. Grimes 	 */
959df8bae1dSRodney W. Grimes 	ip->ip_len = next;
9606effc713SDoug Rabson 	ip->ip_src = fp->ipq_src;
9616effc713SDoug Rabson 	ip->ip_dst = fp->ipq_dst;
962df8bae1dSRodney W. Grimes 	remque(fp);
963194a213eSAndrey A. Chernov 	nipq--;
964df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
9656effc713SDoug Rabson 	m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
9666effc713SDoug Rabson 	m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);
967df8bae1dSRodney W. Grimes 	/* some debugging cruft by sklower, below, will go away soon */
968df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
969df8bae1dSRodney W. Grimes 		register int plen = 0;
9706a800098SYoshinobu Inoue 		for (t = m; t; t = t->m_next)
9716a800098SYoshinobu Inoue 			plen += t->m_len;
9726a800098SYoshinobu Inoue 		m->m_pkthdr.len = plen;
973df8bae1dSRodney W. Grimes 	}
9746a800098SYoshinobu Inoue 	return (m);
975df8bae1dSRodney W. Grimes 
976df8bae1dSRodney W. Grimes dropfrag:
977efe39c6aSJulian Elischer #ifdef IPDIVERT
9788948e4baSArchie Cobbs 	*divinfo = 0;
9798948e4baSArchie Cobbs 	*divcookie = 0;
980efe39c6aSJulian Elischer #endif
981df8bae1dSRodney W. Grimes 	ipstat.ips_fragdropped++;
982df8bae1dSRodney W. Grimes 	m_freem(m);
983df8bae1dSRodney W. Grimes 	return (0);
9846effc713SDoug Rabson 
9856effc713SDoug Rabson #undef GETIP
986df8bae1dSRodney W. Grimes }
987df8bae1dSRodney W. Grimes 
988df8bae1dSRodney W. Grimes /*
989df8bae1dSRodney W. Grimes  * Free a fragment reassembly header and all
990df8bae1dSRodney W. Grimes  * associated datagrams.
991df8bae1dSRodney W. Grimes  */
9920312fbe9SPoul-Henning Kamp static void
993df8bae1dSRodney W. Grimes ip_freef(fp)
994df8bae1dSRodney W. Grimes 	struct ipq *fp;
995df8bae1dSRodney W. Grimes {
9966effc713SDoug Rabson 	register struct mbuf *q;
997df8bae1dSRodney W. Grimes 
9986effc713SDoug Rabson 	while (fp->ipq_frags) {
9996effc713SDoug Rabson 		q = fp->ipq_frags;
10006effc713SDoug Rabson 		fp->ipq_frags = q->m_nextpkt;
10016effc713SDoug Rabson 		m_freem(q);
1002df8bae1dSRodney W. Grimes 	}
1003df8bae1dSRodney W. Grimes 	remque(fp);
1004df8bae1dSRodney W. Grimes 	(void) m_free(dtom(fp));
1005194a213eSAndrey A. Chernov 	nipq--;
1006df8bae1dSRodney W. Grimes }
1007df8bae1dSRodney W. Grimes 
1008df8bae1dSRodney W. Grimes /*
1009df8bae1dSRodney W. Grimes  * IP timer processing;
1010df8bae1dSRodney W. Grimes  * if a timer expires on a reassembly
1011df8bae1dSRodney W. Grimes  * queue, discard it.
1012df8bae1dSRodney W. Grimes  */
1013df8bae1dSRodney W. Grimes void
1014df8bae1dSRodney W. Grimes ip_slowtimo()
1015df8bae1dSRodney W. Grimes {
1016df8bae1dSRodney W. Grimes 	register struct ipq *fp;
1017df8bae1dSRodney W. Grimes 	int s = splnet();
1018194a213eSAndrey A. Chernov 	int i;
1019df8bae1dSRodney W. Grimes 
1020194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
1021194a213eSAndrey A. Chernov 		fp = ipq[i].next;
1022194a213eSAndrey A. Chernov 		if (fp == 0)
1023194a213eSAndrey A. Chernov 			continue;
1024194a213eSAndrey A. Chernov 		while (fp != &ipq[i]) {
1025df8bae1dSRodney W. Grimes 			--fp->ipq_ttl;
1026df8bae1dSRodney W. Grimes 			fp = fp->next;
1027df8bae1dSRodney W. Grimes 			if (fp->prev->ipq_ttl == 0) {
1028df8bae1dSRodney W. Grimes 				ipstat.ips_fragtimeout++;
1029df8bae1dSRodney W. Grimes 				ip_freef(fp->prev);
1030df8bae1dSRodney W. Grimes 			}
1031df8bae1dSRodney W. Grimes 		}
1032194a213eSAndrey A. Chernov 	}
10331f91d8c5SDavid Greenman 	ipflow_slowtimo();
1034df8bae1dSRodney W. Grimes 	splx(s);
1035df8bae1dSRodney W. Grimes }
1036df8bae1dSRodney W. Grimes 
1037df8bae1dSRodney W. Grimes /*
1038df8bae1dSRodney W. Grimes  * Drain off all datagram fragments.
1039df8bae1dSRodney W. Grimes  */
1040df8bae1dSRodney W. Grimes void
1041df8bae1dSRodney W. Grimes ip_drain()
1042df8bae1dSRodney W. Grimes {
1043194a213eSAndrey A. Chernov 	int     i;
1044ce29ab3aSGarrett Wollman 
1045194a213eSAndrey A. Chernov 	for (i = 0; i < IPREASS_NHASH; i++) {
1046194a213eSAndrey A. Chernov 		while (ipq[i].next != &ipq[i]) {
1047194a213eSAndrey A. Chernov 			ipstat.ips_fragdropped++;
1048194a213eSAndrey A. Chernov 			ip_freef(ipq[i].next);
1049194a213eSAndrey A. Chernov 		}
1050194a213eSAndrey A. Chernov 	}
1051ce29ab3aSGarrett Wollman 	in_rtqdrain();
1052df8bae1dSRodney W. Grimes }
1053df8bae1dSRodney W. Grimes 
1054df8bae1dSRodney W. Grimes /*
1055df8bae1dSRodney W. Grimes  * Do option processing on a datagram,
1056df8bae1dSRodney W. Grimes  * possibly discarding it if bad options are encountered,
1057df8bae1dSRodney W. Grimes  * or forwarding it if source-routed.
1058df8bae1dSRodney W. Grimes  * Returns 1 if packet has been forwarded/freed,
1059df8bae1dSRodney W. Grimes  * 0 if the packet should be processed further.
1060df8bae1dSRodney W. Grimes  */
10610312fbe9SPoul-Henning Kamp static int
1062df8bae1dSRodney W. Grimes ip_dooptions(m)
1063df8bae1dSRodney W. Grimes 	struct mbuf *m;
1064df8bae1dSRodney W. Grimes {
1065df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1066df8bae1dSRodney W. Grimes 	register u_char *cp;
1067df8bae1dSRodney W. Grimes 	register struct ip_timestamp *ipt;
1068df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
1069df8bae1dSRodney W. Grimes 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
1070df8bae1dSRodney W. Grimes 	struct in_addr *sin, dst;
1071df8bae1dSRodney W. Grimes 	n_time ntime;
1072df8bae1dSRodney W. Grimes 
1073df8bae1dSRodney W. Grimes 	dst = ip->ip_dst;
1074df8bae1dSRodney W. Grimes 	cp = (u_char *)(ip + 1);
107558938916SGarrett Wollman 	cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1076df8bae1dSRodney W. Grimes 	for (; cnt > 0; cnt -= optlen, cp += optlen) {
1077df8bae1dSRodney W. Grimes 		opt = cp[IPOPT_OPTVAL];
1078df8bae1dSRodney W. Grimes 		if (opt == IPOPT_EOL)
1079df8bae1dSRodney W. Grimes 			break;
1080df8bae1dSRodney W. Grimes 		if (opt == IPOPT_NOP)
1081df8bae1dSRodney W. Grimes 			optlen = 1;
1082df8bae1dSRodney W. Grimes 		else {
1083fdcb8debSJun-ichiro itojun Hagino 			if (cnt < IPOPT_OLEN + sizeof(*cp)) {
1084fdcb8debSJun-ichiro itojun Hagino 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1085fdcb8debSJun-ichiro itojun Hagino 				goto bad;
1086fdcb8debSJun-ichiro itojun Hagino 			}
1087df8bae1dSRodney W. Grimes 			optlen = cp[IPOPT_OLEN];
1088707d00a3SJonathan Lemon 			if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {
1089df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OLEN] - (u_char *)ip;
1090df8bae1dSRodney W. Grimes 				goto bad;
1091df8bae1dSRodney W. Grimes 			}
1092df8bae1dSRodney W. Grimes 		}
1093df8bae1dSRodney W. Grimes 		switch (opt) {
1094df8bae1dSRodney W. Grimes 
1095df8bae1dSRodney W. Grimes 		default:
1096df8bae1dSRodney W. Grimes 			break;
1097df8bae1dSRodney W. Grimes 
1098df8bae1dSRodney W. Grimes 		/*
1099df8bae1dSRodney W. Grimes 		 * Source routing with record.
1100df8bae1dSRodney W. Grimes 		 * Find interface with current destination address.
1101df8bae1dSRodney W. Grimes 		 * If none on this machine then drop if strictly routed,
1102df8bae1dSRodney W. Grimes 		 * or do nothing if loosely routed.
1103df8bae1dSRodney W. Grimes 		 * Record interface address and bring up next address
1104df8bae1dSRodney W. Grimes 		 * component.  If strictly routed make sure next
1105df8bae1dSRodney W. Grimes 		 * address is on directly accessible net.
1106df8bae1dSRodney W. Grimes 		 */
1107df8bae1dSRodney W. Grimes 		case IPOPT_LSRR:
1108df8bae1dSRodney W. Grimes 		case IPOPT_SSRR:
1109df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1110df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1111df8bae1dSRodney W. Grimes 				goto bad;
1112df8bae1dSRodney W. Grimes 			}
1113df8bae1dSRodney W. Grimes 			ipaddr.sin_addr = ip->ip_dst;
1114df8bae1dSRodney W. Grimes 			ia = (struct in_ifaddr *)
1115df8bae1dSRodney W. Grimes 				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
1116df8bae1dSRodney W. Grimes 			if (ia == 0) {
1117df8bae1dSRodney W. Grimes 				if (opt == IPOPT_SSRR) {
1118df8bae1dSRodney W. Grimes 					type = ICMP_UNREACH;
1119df8bae1dSRodney W. Grimes 					code = ICMP_UNREACH_SRCFAIL;
1120df8bae1dSRodney W. Grimes 					goto bad;
1121df8bae1dSRodney W. Grimes 				}
1122bc189bf8SGuido van Rooij 				if (!ip_dosourceroute)
1123bc189bf8SGuido van Rooij 					goto nosourcerouting;
1124df8bae1dSRodney W. Grimes 				/*
1125df8bae1dSRodney W. Grimes 				 * Loose routing, and not at next destination
1126df8bae1dSRodney W. Grimes 				 * yet; nothing to do except forward.
1127df8bae1dSRodney W. Grimes 				 */
1128df8bae1dSRodney W. Grimes 				break;
1129df8bae1dSRodney W. Grimes 			}
1130df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
11315d5d5fc0SJonathan Lemon 			if (off > optlen - (int)sizeof(struct in_addr)) {
1132df8bae1dSRodney W. Grimes 				/*
1133df8bae1dSRodney W. Grimes 				 * End of source route.  Should be for us.
1134df8bae1dSRodney W. Grimes 				 */
11354fce5804SGuido van Rooij 				if (!ip_acceptsourceroute)
11364fce5804SGuido van Rooij 					goto nosourcerouting;
1137df8bae1dSRodney W. Grimes 				save_rte(cp, ip->ip_src);
1138df8bae1dSRodney W. Grimes 				break;
1139df8bae1dSRodney W. Grimes 			}
11401025071fSGarrett Wollman 
11411025071fSGarrett Wollman 			if (!ip_dosourceroute) {
11420af8d3ecSDavid Greenman 				if (ipforwarding) {
11430af8d3ecSDavid Greenman 					char buf[16]; /* aaa.bbb.ccc.ddd\0 */
11440af8d3ecSDavid Greenman 					/*
11450af8d3ecSDavid Greenman 					 * Acting as a router, so generate ICMP
11460af8d3ecSDavid Greenman 					 */
1147efa48587SGuido van Rooij nosourcerouting:
1148bc189bf8SGuido van Rooij 					strcpy(buf, inet_ntoa(ip->ip_dst));
11491025071fSGarrett Wollman 					log(LOG_WARNING,
11501025071fSGarrett Wollman 					    "attempted source route from %s to %s\n",
11511025071fSGarrett Wollman 					    inet_ntoa(ip->ip_src), buf);
11521025071fSGarrett Wollman 					type = ICMP_UNREACH;
11531025071fSGarrett Wollman 					code = ICMP_UNREACH_SRCFAIL;
11541025071fSGarrett Wollman 					goto bad;
11550af8d3ecSDavid Greenman 				} else {
11560af8d3ecSDavid Greenman 					/*
11570af8d3ecSDavid Greenman 					 * Not acting as a router, so silently drop.
11580af8d3ecSDavid Greenman 					 */
11590af8d3ecSDavid Greenman 					ipstat.ips_cantforward++;
11600af8d3ecSDavid Greenman 					m_freem(m);
11610af8d3ecSDavid Greenman 					return (1);
11620af8d3ecSDavid Greenman 				}
11631025071fSGarrett Wollman 			}
11641025071fSGarrett Wollman 
1165df8bae1dSRodney W. Grimes 			/*
1166df8bae1dSRodney W. Grimes 			 * locate outgoing interface
1167df8bae1dSRodney W. Grimes 			 */
116894a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, cp + off,
1169df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
11701025071fSGarrett Wollman 
1171df8bae1dSRodney W. Grimes 			if (opt == IPOPT_SSRR) {
1172df8bae1dSRodney W. Grimes #define	INA	struct in_ifaddr *
1173df8bae1dSRodney W. Grimes #define	SA	struct sockaddr *
1174df8bae1dSRodney W. Grimes 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
1175df8bae1dSRodney W. Grimes 				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
1176df8bae1dSRodney W. Grimes 			} else
1177df8bae1dSRodney W. Grimes 				ia = ip_rtaddr(ipaddr.sin_addr);
1178df8bae1dSRodney W. Grimes 			if (ia == 0) {
1179df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1180df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_SRCFAIL;
1181df8bae1dSRodney W. Grimes 				goto bad;
1182df8bae1dSRodney W. Grimes 			}
1183df8bae1dSRodney W. Grimes 			ip->ip_dst = ipaddr.sin_addr;
118494a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
118594a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1186df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1187df8bae1dSRodney W. Grimes 			/*
1188df8bae1dSRodney W. Grimes 			 * Let ip_intr's mcast routing check handle mcast pkts
1189df8bae1dSRodney W. Grimes 			 */
1190df8bae1dSRodney W. Grimes 			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
1191df8bae1dSRodney W. Grimes 			break;
1192df8bae1dSRodney W. Grimes 
1193df8bae1dSRodney W. Grimes 		case IPOPT_RR:
1194707d00a3SJonathan Lemon 			if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
1195707d00a3SJonathan Lemon 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1196707d00a3SJonathan Lemon 				goto bad;
1197707d00a3SJonathan Lemon 			}
1198df8bae1dSRodney W. Grimes 			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
1199df8bae1dSRodney W. Grimes 				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
1200df8bae1dSRodney W. Grimes 				goto bad;
1201df8bae1dSRodney W. Grimes 			}
1202df8bae1dSRodney W. Grimes 			/*
1203df8bae1dSRodney W. Grimes 			 * If no space remains, ignore.
1204df8bae1dSRodney W. Grimes 			 */
1205df8bae1dSRodney W. Grimes 			off--;			/* 0 origin */
12065d5d5fc0SJonathan Lemon 			if (off > optlen - (int)sizeof(struct in_addr))
1207df8bae1dSRodney W. Grimes 				break;
120894a5d9b6SDavid Greenman 			(void)memcpy(&ipaddr.sin_addr, &ip->ip_dst,
1209df8bae1dSRodney W. Grimes 			    sizeof(ipaddr.sin_addr));
1210df8bae1dSRodney W. Grimes 			/*
1211df8bae1dSRodney W. Grimes 			 * locate outgoing interface; if we're the destination,
1212df8bae1dSRodney W. Grimes 			 * use the incoming interface (should be same).
1213df8bae1dSRodney W. Grimes 			 */
1214df8bae1dSRodney W. Grimes 			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
1215df8bae1dSRodney W. Grimes 			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
1216df8bae1dSRodney W. Grimes 				type = ICMP_UNREACH;
1217df8bae1dSRodney W. Grimes 				code = ICMP_UNREACH_HOST;
1218df8bae1dSRodney W. Grimes 				goto bad;
1219df8bae1dSRodney W. Grimes 			}
122094a5d9b6SDavid Greenman 			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
122194a5d9b6SDavid Greenman 			    sizeof(struct in_addr));
1222df8bae1dSRodney W. Grimes 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
1223df8bae1dSRodney W. Grimes 			break;
1224df8bae1dSRodney W. Grimes 
1225df8bae1dSRodney W. Grimes 		case IPOPT_TS:
1226df8bae1dSRodney W. Grimes 			code = cp - (u_char *)ip;
1227df8bae1dSRodney W. Grimes 			ipt = (struct ip_timestamp *)cp;
1228df8bae1dSRodney W. Grimes 			if (ipt->ipt_len < 5)
1229df8bae1dSRodney W. Grimes 				goto bad;
12305d5d5fc0SJonathan Lemon 			if (ipt->ipt_ptr >
12315d5d5fc0SJonathan Lemon 			    ipt->ipt_len - (int)sizeof(int32_t)) {
1232df8bae1dSRodney W. Grimes 				if (++ipt->ipt_oflw == 0)
1233df8bae1dSRodney W. Grimes 					goto bad;
1234df8bae1dSRodney W. Grimes 				break;
1235df8bae1dSRodney W. Grimes 			}
1236df8bae1dSRodney W. Grimes 			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
1237df8bae1dSRodney W. Grimes 			switch (ipt->ipt_flg) {
1238df8bae1dSRodney W. Grimes 
1239df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSONLY:
1240df8bae1dSRodney W. Grimes 				break;
1241df8bae1dSRodney W. Grimes 
1242df8bae1dSRodney W. Grimes 			case IPOPT_TS_TSANDADDR:
1243b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1244df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1245df8bae1dSRodney W. Grimes 					goto bad;
1246df8bae1dSRodney W. Grimes 				ipaddr.sin_addr = dst;
1247df8bae1dSRodney W. Grimes 				ia = (INA)ifaof_ifpforaddr((SA)&ipaddr,
1248df8bae1dSRodney W. Grimes 							    m->m_pkthdr.rcvif);
1249df8bae1dSRodney W. Grimes 				if (ia == 0)
1250df8bae1dSRodney W. Grimes 					continue;
125194a5d9b6SDavid Greenman 				(void)memcpy(sin, &IA_SIN(ia)->sin_addr,
125294a5d9b6SDavid Greenman 				    sizeof(struct in_addr));
1253df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1254df8bae1dSRodney W. Grimes 				break;
1255df8bae1dSRodney W. Grimes 
1256df8bae1dSRodney W. Grimes 			case IPOPT_TS_PRESPEC:
1257b8e8c209SDavid Greenman 				if (ipt->ipt_ptr - 1 + sizeof(n_time) +
1258df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr) > ipt->ipt_len)
1259df8bae1dSRodney W. Grimes 					goto bad;
126094a5d9b6SDavid Greenman 				(void)memcpy(&ipaddr.sin_addr, sin,
1261df8bae1dSRodney W. Grimes 				    sizeof(struct in_addr));
1262df8bae1dSRodney W. Grimes 				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
1263df8bae1dSRodney W. Grimes 					continue;
1264df8bae1dSRodney W. Grimes 				ipt->ipt_ptr += sizeof(struct in_addr);
1265df8bae1dSRodney W. Grimes 				break;
1266df8bae1dSRodney W. Grimes 
1267df8bae1dSRodney W. Grimes 			default:
1268df8bae1dSRodney W. Grimes 				goto bad;
1269df8bae1dSRodney W. Grimes 			}
1270df8bae1dSRodney W. Grimes 			ntime = iptime();
127194a5d9b6SDavid Greenman 			(void)memcpy(cp + ipt->ipt_ptr - 1, &ntime,
1272df8bae1dSRodney W. Grimes 			    sizeof(n_time));
1273df8bae1dSRodney W. Grimes 			ipt->ipt_ptr += sizeof(n_time);
1274df8bae1dSRodney W. Grimes 		}
1275df8bae1dSRodney W. Grimes 	}
127647174b49SAndrey A. Chernov 	if (forward && ipforwarding) {
1277df8bae1dSRodney W. Grimes 		ip_forward(m, 1);
1278df8bae1dSRodney W. Grimes 		return (1);
1279df8bae1dSRodney W. Grimes 	}
1280df8bae1dSRodney W. Grimes 	return (0);
1281df8bae1dSRodney W. Grimes bad:
1282df8bae1dSRodney W. Grimes 	icmp_error(m, type, code, 0, 0);
1283df8bae1dSRodney W. Grimes 	ipstat.ips_badoptions++;
1284df8bae1dSRodney W. Grimes 	return (1);
1285df8bae1dSRodney W. Grimes }
1286df8bae1dSRodney W. Grimes 
1287df8bae1dSRodney W. Grimes /*
1288df8bae1dSRodney W. Grimes  * Given address of next destination (final or next hop),
1289df8bae1dSRodney W. Grimes  * return internet address info of interface to be used to get there.
1290df8bae1dSRodney W. Grimes  */
12910312fbe9SPoul-Henning Kamp static struct in_ifaddr *
1292df8bae1dSRodney W. Grimes ip_rtaddr(dst)
1293df8bae1dSRodney W. Grimes 	 struct in_addr dst;
1294df8bae1dSRodney W. Grimes {
1295df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1296df8bae1dSRodney W. Grimes 
1297df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *) &ipforward_rt.ro_dst;
1298df8bae1dSRodney W. Grimes 
1299df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) {
1300df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1301df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1302df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1303df8bae1dSRodney W. Grimes 		}
1304df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1305df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1306df8bae1dSRodney W. Grimes 		sin->sin_addr = dst;
1307df8bae1dSRodney W. Grimes 
13082c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1309df8bae1dSRodney W. Grimes 	}
1310df8bae1dSRodney W. Grimes 	if (ipforward_rt.ro_rt == 0)
1311df8bae1dSRodney W. Grimes 		return ((struct in_ifaddr *)0);
1312df8bae1dSRodney W. Grimes 	return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa);
1313df8bae1dSRodney W. Grimes }
1314df8bae1dSRodney W. Grimes 
1315df8bae1dSRodney W. Grimes /*
1316df8bae1dSRodney W. Grimes  * Save incoming source route for use in replies,
1317df8bae1dSRodney W. Grimes  * to be picked up later by ip_srcroute if the receiver is interested.
1318df8bae1dSRodney W. Grimes  */
1319df8bae1dSRodney W. Grimes void
1320df8bae1dSRodney W. Grimes save_rte(option, dst)
1321df8bae1dSRodney W. Grimes 	u_char *option;
1322df8bae1dSRodney W. Grimes 	struct in_addr dst;
1323df8bae1dSRodney W. Grimes {
1324df8bae1dSRodney W. Grimes 	unsigned olen;
1325df8bae1dSRodney W. Grimes 
1326df8bae1dSRodney W. Grimes 	olen = option[IPOPT_OLEN];
1327df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1328df8bae1dSRodney W. Grimes 	if (ipprintfs)
1329df8bae1dSRodney W. Grimes 		printf("save_rte: olen %d\n", olen);
1330df8bae1dSRodney W. Grimes #endif
1331df8bae1dSRodney W. Grimes 	if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst)))
1332df8bae1dSRodney W. Grimes 		return;
13330453d3cbSBruce Evans 	bcopy(option, ip_srcrt.srcopt, olen);
1334df8bae1dSRodney W. Grimes 	ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr);
1335df8bae1dSRodney W. Grimes 	ip_srcrt.dst = dst;
1336df8bae1dSRodney W. Grimes }
1337df8bae1dSRodney W. Grimes 
1338df8bae1dSRodney W. Grimes /*
1339df8bae1dSRodney W. Grimes  * Retrieve incoming source route for use in replies,
1340df8bae1dSRodney W. Grimes  * in the same form used by setsockopt.
1341df8bae1dSRodney W. Grimes  * The first hop is placed before the options, will be removed later.
1342df8bae1dSRodney W. Grimes  */
1343df8bae1dSRodney W. Grimes struct mbuf *
1344df8bae1dSRodney W. Grimes ip_srcroute()
1345df8bae1dSRodney W. Grimes {
1346df8bae1dSRodney W. Grimes 	register struct in_addr *p, *q;
1347df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1348df8bae1dSRodney W. Grimes 
1349df8bae1dSRodney W. Grimes 	if (ip_nhops == 0)
1350df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1351cfe8b629SGarrett Wollman 	m = m_get(M_DONTWAIT, MT_HEADER);
1352df8bae1dSRodney W. Grimes 	if (m == 0)
1353df8bae1dSRodney W. Grimes 		return ((struct mbuf *)0);
1354df8bae1dSRodney W. Grimes 
1355df8bae1dSRodney W. Grimes #define OPTSIZ	(sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt))
1356df8bae1dSRodney W. Grimes 
1357df8bae1dSRodney W. Grimes 	/* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */
1358df8bae1dSRodney W. Grimes 	m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) +
1359df8bae1dSRodney W. Grimes 	    OPTSIZ;
1360df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1361df8bae1dSRodney W. Grimes 	if (ipprintfs)
1362df8bae1dSRodney W. Grimes 		printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len);
1363df8bae1dSRodney W. Grimes #endif
1364df8bae1dSRodney W. Grimes 
1365df8bae1dSRodney W. Grimes 	/*
1366df8bae1dSRodney W. Grimes 	 * First save first hop for return route
1367df8bae1dSRodney W. Grimes 	 */
1368df8bae1dSRodney W. Grimes 	p = &ip_srcrt.route[ip_nhops - 1];
1369df8bae1dSRodney W. Grimes 	*(mtod(m, struct in_addr *)) = *p--;
1370df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1371df8bae1dSRodney W. Grimes 	if (ipprintfs)
1372af38c68cSLuigi Rizzo 		printf(" hops %lx", (u_long)ntohl(mtod(m, struct in_addr *)->s_addr));
1373df8bae1dSRodney W. Grimes #endif
1374df8bae1dSRodney W. Grimes 
1375df8bae1dSRodney W. Grimes 	/*
1376df8bae1dSRodney W. Grimes 	 * Copy option fields and padding (nop) to mbuf.
1377df8bae1dSRodney W. Grimes 	 */
1378df8bae1dSRodney W. Grimes 	ip_srcrt.nop = IPOPT_NOP;
1379df8bae1dSRodney W. Grimes 	ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF;
138094a5d9b6SDavid Greenman 	(void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr),
138194a5d9b6SDavid Greenman 	    &ip_srcrt.nop, OPTSIZ);
1382df8bae1dSRodney W. Grimes 	q = (struct in_addr *)(mtod(m, caddr_t) +
1383df8bae1dSRodney W. Grimes 	    sizeof(struct in_addr) + OPTSIZ);
1384df8bae1dSRodney W. Grimes #undef OPTSIZ
1385df8bae1dSRodney W. Grimes 	/*
1386df8bae1dSRodney W. Grimes 	 * Record return path as an IP source route,
1387df8bae1dSRodney W. Grimes 	 * reversing the path (pointers are now aligned).
1388df8bae1dSRodney W. Grimes 	 */
1389df8bae1dSRodney W. Grimes 	while (p >= ip_srcrt.route) {
1390df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1391df8bae1dSRodney W. Grimes 		if (ipprintfs)
1392af38c68cSLuigi Rizzo 			printf(" %lx", (u_long)ntohl(q->s_addr));
1393df8bae1dSRodney W. Grimes #endif
1394df8bae1dSRodney W. Grimes 		*q++ = *p--;
1395df8bae1dSRodney W. Grimes 	}
1396df8bae1dSRodney W. Grimes 	/*
1397df8bae1dSRodney W. Grimes 	 * Last hop goes to final destination.
1398df8bae1dSRodney W. Grimes 	 */
1399df8bae1dSRodney W. Grimes 	*q = ip_srcrt.dst;
1400df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1401df8bae1dSRodney W. Grimes 	if (ipprintfs)
1402af38c68cSLuigi Rizzo 		printf(" %lx\n", (u_long)ntohl(q->s_addr));
1403df8bae1dSRodney W. Grimes #endif
1404df8bae1dSRodney W. Grimes 	return (m);
1405df8bae1dSRodney W. Grimes }
1406df8bae1dSRodney W. Grimes 
1407df8bae1dSRodney W. Grimes /*
1408df8bae1dSRodney W. Grimes  * Strip out IP options, at higher
1409df8bae1dSRodney W. Grimes  * level protocol in the kernel.
1410df8bae1dSRodney W. Grimes  * Second argument is buffer to which options
1411df8bae1dSRodney W. Grimes  * will be moved, and return value is their length.
1412df8bae1dSRodney W. Grimes  * XXX should be deleted; last arg currently ignored.
1413df8bae1dSRodney W. Grimes  */
1414df8bae1dSRodney W. Grimes void
1415df8bae1dSRodney W. Grimes ip_stripoptions(m, mopt)
1416df8bae1dSRodney W. Grimes 	register struct mbuf *m;
1417df8bae1dSRodney W. Grimes 	struct mbuf *mopt;
1418df8bae1dSRodney W. Grimes {
1419df8bae1dSRodney W. Grimes 	register int i;
1420df8bae1dSRodney W. Grimes 	struct ip *ip = mtod(m, struct ip *);
1421df8bae1dSRodney W. Grimes 	register caddr_t opts;
1422df8bae1dSRodney W. Grimes 	int olen;
1423df8bae1dSRodney W. Grimes 
142458938916SGarrett Wollman 	olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip);
1425df8bae1dSRodney W. Grimes 	opts = (caddr_t)(ip + 1);
1426df8bae1dSRodney W. Grimes 	i = m->m_len - (sizeof (struct ip) + olen);
1427df8bae1dSRodney W. Grimes 	bcopy(opts + olen, opts, (unsigned)i);
1428df8bae1dSRodney W. Grimes 	m->m_len -= olen;
1429df8bae1dSRodney W. Grimes 	if (m->m_flags & M_PKTHDR)
1430df8bae1dSRodney W. Grimes 		m->m_pkthdr.len -= olen;
143158938916SGarrett Wollman 	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
1432df8bae1dSRodney W. Grimes }
1433df8bae1dSRodney W. Grimes 
1434df8bae1dSRodney W. Grimes u_char inetctlerrmap[PRC_NCMDS] = {
1435df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1436df8bae1dSRodney W. Grimes 	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
1437df8bae1dSRodney W. Grimes 	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
1438df8bae1dSRodney W. Grimes 	EMSGSIZE,	EHOSTUNREACH,	0,		0,
1439df8bae1dSRodney W. Grimes 	0,		0,		0,		0,
1440e4bb5b05SJonathan Lemon 	ENOPROTOOPT,	ENETRESET
1441df8bae1dSRodney W. Grimes };
1442df8bae1dSRodney W. Grimes 
1443df8bae1dSRodney W. Grimes /*
1444df8bae1dSRodney W. Grimes  * Forward a packet.  If some error occurs return the sender
1445df8bae1dSRodney W. Grimes  * an icmp packet.  Note we can't always generate a meaningful
1446df8bae1dSRodney W. Grimes  * icmp message because icmp doesn't have a large enough repertoire
1447df8bae1dSRodney W. Grimes  * of codes and types.
1448df8bae1dSRodney W. Grimes  *
1449df8bae1dSRodney W. Grimes  * If not forwarding, just drop the packet.  This could be confusing
1450df8bae1dSRodney W. Grimes  * if ipforwarding was zero but some routing protocol was advancing
1451df8bae1dSRodney W. Grimes  * us as a gateway to somewhere.  However, we must let the routing
1452df8bae1dSRodney W. Grimes  * protocol deal with that.
1453df8bae1dSRodney W. Grimes  *
1454df8bae1dSRodney W. Grimes  * The srcrt parameter indicates whether the packet is being forwarded
1455df8bae1dSRodney W. Grimes  * via a source route.
1456df8bae1dSRodney W. Grimes  */
14570312fbe9SPoul-Henning Kamp static void
1458df8bae1dSRodney W. Grimes ip_forward(m, srcrt)
1459df8bae1dSRodney W. Grimes 	struct mbuf *m;
1460df8bae1dSRodney W. Grimes 	int srcrt;
1461df8bae1dSRodney W. Grimes {
1462df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
1463df8bae1dSRodney W. Grimes 	register struct sockaddr_in *sin;
1464df8bae1dSRodney W. Grimes 	register struct rtentry *rt;
146526f9a767SRodney W. Grimes 	int error, type = 0, code = 0;
1466df8bae1dSRodney W. Grimes 	struct mbuf *mcopy;
1467df8bae1dSRodney W. Grimes 	n_long dest;
1468df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
14696a800098SYoshinobu Inoue #ifdef IPSEC
14706a800098SYoshinobu Inoue 	struct ifnet dummyifp;
14716a800098SYoshinobu Inoue #endif
1472df8bae1dSRodney W. Grimes 
1473df8bae1dSRodney W. Grimes 	dest = 0;
1474df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1475df8bae1dSRodney W. Grimes 	if (ipprintfs)
147661ce519bSPoul-Henning Kamp 		printf("forward: src %lx dst %lx ttl %x\n",
1477162886e2SBruce Evans 		    (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr,
1478162886e2SBruce Evans 		    ip->ip_ttl);
1479df8bae1dSRodney W. Grimes #endif
1480100ba1a6SJordan K. Hubbard 
1481100ba1a6SJordan K. Hubbard 
148292af003dSGarrett Wollman 	if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) {
1483df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1484df8bae1dSRodney W. Grimes 		m_freem(m);
1485df8bae1dSRodney W. Grimes 		return;
1486df8bae1dSRodney W. Grimes 	}
14871b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
14881b968362SDag-Erling Smørgrav 	if (!ipstealth) {
14891b968362SDag-Erling Smørgrav #endif
1490df8bae1dSRodney W. Grimes 		if (ip->ip_ttl <= IPTTLDEC) {
14911b968362SDag-Erling Smørgrav 			icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
14921b968362SDag-Erling Smørgrav 			    dest, 0);
1493df8bae1dSRodney W. Grimes 			return;
1494df8bae1dSRodney W. Grimes 		}
14951b968362SDag-Erling Smørgrav #ifdef IPSTEALTH
14961b968362SDag-Erling Smørgrav 	}
14971b968362SDag-Erling Smørgrav #endif
1498df8bae1dSRodney W. Grimes 
1499df8bae1dSRodney W. Grimes 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
1500df8bae1dSRodney W. Grimes 	if ((rt = ipforward_rt.ro_rt) == 0 ||
1501df8bae1dSRodney W. Grimes 	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
1502df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt) {
1503df8bae1dSRodney W. Grimes 			RTFREE(ipforward_rt.ro_rt);
1504df8bae1dSRodney W. Grimes 			ipforward_rt.ro_rt = 0;
1505df8bae1dSRodney W. Grimes 		}
1506df8bae1dSRodney W. Grimes 		sin->sin_family = AF_INET;
1507df8bae1dSRodney W. Grimes 		sin->sin_len = sizeof(*sin);
1508df8bae1dSRodney W. Grimes 		sin->sin_addr = ip->ip_dst;
1509df8bae1dSRodney W. Grimes 
15102c17fe93SGarrett Wollman 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
1511df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt == 0) {
1512df8bae1dSRodney W. Grimes 			icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
1513df8bae1dSRodney W. Grimes 			return;
1514df8bae1dSRodney W. Grimes 		}
1515df8bae1dSRodney W. Grimes 		rt = ipforward_rt.ro_rt;
1516df8bae1dSRodney W. Grimes 	}
1517df8bae1dSRodney W. Grimes 
1518df8bae1dSRodney W. Grimes 	/*
1519df8bae1dSRodney W. Grimes 	 * Save at most 64 bytes of the packet in case
1520df8bae1dSRodney W. Grimes 	 * we need to generate an ICMP message to the src.
1521df8bae1dSRodney W. Grimes 	 */
1522df8bae1dSRodney W. Grimes 	mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64));
152304287599SRuslan Ermilov 	if (mcopy && (mcopy->m_flags & M_EXT))
152404287599SRuslan Ermilov 		m_copydata(mcopy, 0, sizeof(struct ip), mtod(mcopy, caddr_t));
152504287599SRuslan Ermilov 
152604287599SRuslan Ermilov #ifdef IPSTEALTH
152704287599SRuslan Ermilov 	if (!ipstealth) {
152804287599SRuslan Ermilov #endif
152904287599SRuslan Ermilov 		ip->ip_ttl -= IPTTLDEC;
153004287599SRuslan Ermilov #ifdef IPSTEALTH
153104287599SRuslan Ermilov 	}
153204287599SRuslan Ermilov #endif
1533df8bae1dSRodney W. Grimes 
1534df8bae1dSRodney W. Grimes 	/*
1535df8bae1dSRodney W. Grimes 	 * If forwarding packet using same interface that it came in on,
1536df8bae1dSRodney W. Grimes 	 * perhaps should send a redirect to sender to shortcut a hop.
1537df8bae1dSRodney W. Grimes 	 * Only send redirect if source is sending directly to us,
1538df8bae1dSRodney W. Grimes 	 * and if packet was not source routed (or has any options).
1539df8bae1dSRodney W. Grimes 	 * Also, don't send redirect if forwarding using a default route
1540df8bae1dSRodney W. Grimes 	 * or a route modified by a redirect.
1541df8bae1dSRodney W. Grimes 	 */
1542df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
1543df8bae1dSRodney W. Grimes 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
1544df8bae1dSRodney W. Grimes 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
1545df8bae1dSRodney W. Grimes 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
1546df8bae1dSRodney W. Grimes 	    ipsendredirects && !srcrt) {
1547df8bae1dSRodney W. Grimes #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
1548df8bae1dSRodney W. Grimes 		u_long src = ntohl(ip->ip_src.s_addr);
1549df8bae1dSRodney W. Grimes 
1550df8bae1dSRodney W. Grimes 		if (RTA(rt) &&
1551df8bae1dSRodney W. Grimes 		    (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) {
1552df8bae1dSRodney W. Grimes 		    if (rt->rt_flags & RTF_GATEWAY)
1553df8bae1dSRodney W. Grimes 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
1554df8bae1dSRodney W. Grimes 		    else
1555df8bae1dSRodney W. Grimes 			dest = ip->ip_dst.s_addr;
1556df8bae1dSRodney W. Grimes 		    /* Router requirements says to only send host redirects */
1557df8bae1dSRodney W. Grimes 		    type = ICMP_REDIRECT;
1558df8bae1dSRodney W. Grimes 		    code = ICMP_REDIRECT_HOST;
1559df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
1560df8bae1dSRodney W. Grimes 		    if (ipprintfs)
1561df8bae1dSRodney W. Grimes 		        printf("redirect (%d) to %lx\n", code, (u_long)dest);
1562df8bae1dSRodney W. Grimes #endif
1563df8bae1dSRodney W. Grimes 		}
1564df8bae1dSRodney W. Grimes 	}
1565df8bae1dSRodney W. Grimes 
1566b97d15cbSGarrett Wollman 	error = ip_output(m, (struct mbuf *)0, &ipforward_rt,
1567b97d15cbSGarrett Wollman 			  IP_FORWARDING, 0);
1568df8bae1dSRodney W. Grimes 	if (error)
1569df8bae1dSRodney W. Grimes 		ipstat.ips_cantforward++;
1570df8bae1dSRodney W. Grimes 	else {
1571df8bae1dSRodney W. Grimes 		ipstat.ips_forward++;
1572df8bae1dSRodney W. Grimes 		if (type)
1573df8bae1dSRodney W. Grimes 			ipstat.ips_redirectsent++;
1574df8bae1dSRodney W. Grimes 		else {
15751f91d8c5SDavid Greenman 			if (mcopy) {
15761f91d8c5SDavid Greenman 				ipflow_create(&ipforward_rt, mcopy);
1577df8bae1dSRodney W. Grimes 				m_freem(mcopy);
15781f91d8c5SDavid Greenman 			}
1579df8bae1dSRodney W. Grimes 			return;
1580df8bae1dSRodney W. Grimes 		}
1581df8bae1dSRodney W. Grimes 	}
1582df8bae1dSRodney W. Grimes 	if (mcopy == NULL)
1583df8bae1dSRodney W. Grimes 		return;
1584df8bae1dSRodney W. Grimes 	destifp = NULL;
1585df8bae1dSRodney W. Grimes 
1586df8bae1dSRodney W. Grimes 	switch (error) {
1587df8bae1dSRodney W. Grimes 
1588df8bae1dSRodney W. Grimes 	case 0:				/* forwarded, but need redirect */
1589df8bae1dSRodney W. Grimes 		/* type, code set above */
1590df8bae1dSRodney W. Grimes 		break;
1591df8bae1dSRodney W. Grimes 
1592df8bae1dSRodney W. Grimes 	case ENETUNREACH:		/* shouldn't happen, checked above */
1593df8bae1dSRodney W. Grimes 	case EHOSTUNREACH:
1594df8bae1dSRodney W. Grimes 	case ENETDOWN:
1595df8bae1dSRodney W. Grimes 	case EHOSTDOWN:
1596df8bae1dSRodney W. Grimes 	default:
1597df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1598df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_HOST;
1599df8bae1dSRodney W. Grimes 		break;
1600df8bae1dSRodney W. Grimes 
1601df8bae1dSRodney W. Grimes 	case EMSGSIZE:
1602df8bae1dSRodney W. Grimes 		type = ICMP_UNREACH;
1603df8bae1dSRodney W. Grimes 		code = ICMP_UNREACH_NEEDFRAG;
16046a800098SYoshinobu Inoue #ifndef IPSEC
1605df8bae1dSRodney W. Grimes 		if (ipforward_rt.ro_rt)
1606df8bae1dSRodney W. Grimes 			destifp = ipforward_rt.ro_rt->rt_ifp;
16076a800098SYoshinobu Inoue #else
16086a800098SYoshinobu Inoue 		/*
16096a800098SYoshinobu Inoue 		 * If the packet is routed over IPsec tunnel, tell the
16106a800098SYoshinobu Inoue 		 * originator the tunnel MTU.
16116a800098SYoshinobu Inoue 		 *	tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
16126a800098SYoshinobu Inoue 		 * XXX quickhack!!!
16136a800098SYoshinobu Inoue 		 */
16146a800098SYoshinobu Inoue 		if (ipforward_rt.ro_rt) {
16156a800098SYoshinobu Inoue 			struct secpolicy *sp = NULL;
16166a800098SYoshinobu Inoue 			int ipsecerror;
16176a800098SYoshinobu Inoue 			int ipsechdr;
16186a800098SYoshinobu Inoue 			struct route *ro;
16196a800098SYoshinobu Inoue 
16206a800098SYoshinobu Inoue 			sp = ipsec4_getpolicybyaddr(mcopy,
16216a800098SYoshinobu Inoue 						    IPSEC_DIR_OUTBOUND,
16226a800098SYoshinobu Inoue 			                            IP_FORWARDING,
16236a800098SYoshinobu Inoue 			                            &ipsecerror);
16246a800098SYoshinobu Inoue 
16256a800098SYoshinobu Inoue 			if (sp == NULL)
16266a800098SYoshinobu Inoue 				destifp = ipforward_rt.ro_rt->rt_ifp;
16276a800098SYoshinobu Inoue 			else {
16286a800098SYoshinobu Inoue 				/* count IPsec header size */
16296a800098SYoshinobu Inoue 				ipsechdr = ipsec4_hdrsiz(mcopy,
16306a800098SYoshinobu Inoue 							 IPSEC_DIR_OUTBOUND,
16316a800098SYoshinobu Inoue 							 NULL);
16326a800098SYoshinobu Inoue 
16336a800098SYoshinobu Inoue 				/*
16346a800098SYoshinobu Inoue 				 * find the correct route for outer IPv4
16356a800098SYoshinobu Inoue 				 * header, compute tunnel MTU.
16366a800098SYoshinobu Inoue 				 *
16376a800098SYoshinobu Inoue 				 * XXX BUG ALERT
16386a800098SYoshinobu Inoue 				 * The "dummyifp" code relies upon the fact
16396a800098SYoshinobu Inoue 				 * that icmp_error() touches only ifp->if_mtu.
16406a800098SYoshinobu Inoue 				 */
16416a800098SYoshinobu Inoue 				/*XXX*/
16426a800098SYoshinobu Inoue 				destifp = NULL;
16436a800098SYoshinobu Inoue 				if (sp->req != NULL
16446a800098SYoshinobu Inoue 				 && sp->req->sav != NULL
16456a800098SYoshinobu Inoue 				 && sp->req->sav->sah != NULL) {
16466a800098SYoshinobu Inoue 					ro = &sp->req->sav->sah->sa_route;
16476a800098SYoshinobu Inoue 					if (ro->ro_rt && ro->ro_rt->rt_ifp) {
16486a800098SYoshinobu Inoue 						dummyifp.if_mtu =
16496a800098SYoshinobu Inoue 						    ro->ro_rt->rt_ifp->if_mtu;
16506a800098SYoshinobu Inoue 						dummyifp.if_mtu -= ipsechdr;
16516a800098SYoshinobu Inoue 						destifp = &dummyifp;
16526a800098SYoshinobu Inoue 					}
16536a800098SYoshinobu Inoue 				}
16546a800098SYoshinobu Inoue 
16556a800098SYoshinobu Inoue 				key_freesp(sp);
16566a800098SYoshinobu Inoue 			}
16576a800098SYoshinobu Inoue 		}
16586a800098SYoshinobu Inoue #endif /*IPSEC*/
1659df8bae1dSRodney W. Grimes 		ipstat.ips_cantfrag++;
1660df8bae1dSRodney W. Grimes 		break;
1661df8bae1dSRodney W. Grimes 
1662df8bae1dSRodney W. Grimes 	case ENOBUFS:
1663df8bae1dSRodney W. Grimes 		type = ICMP_SOURCEQUENCH;
1664df8bae1dSRodney W. Grimes 		code = 0;
1665df8bae1dSRodney W. Grimes 		break;
16663a06e3e0SRuslan Ermilov 
16673a06e3e0SRuslan Ermilov 	case EACCES:			/* ipfw denied packet */
16683a06e3e0SRuslan Ermilov 		m_freem(mcopy);
16693a06e3e0SRuslan Ermilov 		return;
1670df8bae1dSRodney W. Grimes 	}
167104287599SRuslan Ermilov 	if (mcopy->m_flags & M_EXT)
167204287599SRuslan Ermilov 		m_copyback(mcopy, 0, sizeof(struct ip), mtod(mcopy, caddr_t));
1673df8bae1dSRodney W. Grimes 	icmp_error(mcopy, type, code, dest, destifp);
1674df8bae1dSRodney W. Grimes }
1675df8bae1dSRodney W. Grimes 
167682c23ebaSBill Fenner void
167782c23ebaSBill Fenner ip_savecontrol(inp, mp, ip, m)
167882c23ebaSBill Fenner 	register struct inpcb *inp;
167982c23ebaSBill Fenner 	register struct mbuf **mp;
168082c23ebaSBill Fenner 	register struct ip *ip;
168182c23ebaSBill Fenner 	register struct mbuf *m;
168282c23ebaSBill Fenner {
168382c23ebaSBill Fenner 	if (inp->inp_socket->so_options & SO_TIMESTAMP) {
168482c23ebaSBill Fenner 		struct timeval tv;
168582c23ebaSBill Fenner 
168682c23ebaSBill Fenner 		microtime(&tv);
168782c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv),
168882c23ebaSBill Fenner 			SCM_TIMESTAMP, SOL_SOCKET);
168982c23ebaSBill Fenner 		if (*mp)
169082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
169182c23ebaSBill Fenner 	}
169282c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVDSTADDR) {
169382c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
169482c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
169582c23ebaSBill Fenner 		if (*mp)
169682c23ebaSBill Fenner 			mp = &(*mp)->m_next;
169782c23ebaSBill Fenner 	}
169882c23ebaSBill Fenner #ifdef notyet
169982c23ebaSBill Fenner 	/* XXX
170082c23ebaSBill Fenner 	 * Moving these out of udp_input() made them even more broken
170182c23ebaSBill Fenner 	 * than they already were.
170282c23ebaSBill Fenner 	 */
170382c23ebaSBill Fenner 	/* options were tossed already */
170482c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVOPTS) {
170582c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) opts_deleted_above,
170682c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP);
170782c23ebaSBill Fenner 		if (*mp)
170882c23ebaSBill Fenner 			mp = &(*mp)->m_next;
170982c23ebaSBill Fenner 	}
171082c23ebaSBill Fenner 	/* ip_srcroute doesn't do what we want here, need to fix */
171182c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVRETOPTS) {
171282c23ebaSBill Fenner 		*mp = sbcreatecontrol((caddr_t) ip_srcroute(),
171382c23ebaSBill Fenner 		    sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP);
171482c23ebaSBill Fenner 		if (*mp)
171582c23ebaSBill Fenner 			mp = &(*mp)->m_next;
171682c23ebaSBill Fenner 	}
171782c23ebaSBill Fenner #endif
171882c23ebaSBill Fenner 	if (inp->inp_flags & INP_RECVIF) {
1719d314ad7bSJulian Elischer 		struct ifnet *ifp;
1720d314ad7bSJulian Elischer 		struct sdlbuf {
172182c23ebaSBill Fenner 			struct sockaddr_dl sdl;
1722d314ad7bSJulian Elischer 			u_char	pad[32];
1723d314ad7bSJulian Elischer 		} sdlbuf;
1724d314ad7bSJulian Elischer 		struct sockaddr_dl *sdp;
1725d314ad7bSJulian Elischer 		struct sockaddr_dl *sdl2 = &sdlbuf.sdl;
172682c23ebaSBill Fenner 
1727d314ad7bSJulian Elischer 		if (((ifp = m->m_pkthdr.rcvif))
1728d314ad7bSJulian Elischer 		&& ( ifp->if_index && (ifp->if_index <= if_index))) {
1729d314ad7bSJulian Elischer 			sdp = (struct sockaddr_dl *)(ifnet_addrs
1730d314ad7bSJulian Elischer 					[ifp->if_index - 1]->ifa_addr);
1731d314ad7bSJulian Elischer 			/*
1732d314ad7bSJulian Elischer 			 * Change our mind and don't try copy.
1733d314ad7bSJulian Elischer 			 */
1734d314ad7bSJulian Elischer 			if ((sdp->sdl_family != AF_LINK)
1735d314ad7bSJulian Elischer 			|| (sdp->sdl_len > sizeof(sdlbuf))) {
1736d314ad7bSJulian Elischer 				goto makedummy;
1737d314ad7bSJulian Elischer 			}
1738d314ad7bSJulian Elischer 			bcopy(sdp, sdl2, sdp->sdl_len);
1739d314ad7bSJulian Elischer 		} else {
1740d314ad7bSJulian Elischer makedummy:
1741d314ad7bSJulian Elischer 			sdl2->sdl_len
1742d314ad7bSJulian Elischer 				= offsetof(struct sockaddr_dl, sdl_data[0]);
1743d314ad7bSJulian Elischer 			sdl2->sdl_family = AF_LINK;
1744d314ad7bSJulian Elischer 			sdl2->sdl_index = 0;
1745d314ad7bSJulian Elischer 			sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0;
1746d314ad7bSJulian Elischer 		}
1747d314ad7bSJulian Elischer 		*mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len,
174882c23ebaSBill Fenner 			IP_RECVIF, IPPROTO_IP);
174982c23ebaSBill Fenner 		if (*mp)
175082c23ebaSBill Fenner 			mp = &(*mp)->m_next;
175182c23ebaSBill Fenner 	}
175282c23ebaSBill Fenner }
175382c23ebaSBill Fenner 
1754df8bae1dSRodney W. Grimes int
1755f0068c4aSGarrett Wollman ip_rsvp_init(struct socket *so)
1756f0068c4aSGarrett Wollman {
1757f0068c4aSGarrett Wollman 	if (so->so_type != SOCK_RAW ||
1758f0068c4aSGarrett Wollman 	    so->so_proto->pr_protocol != IPPROTO_RSVP)
1759f0068c4aSGarrett Wollman 	  return EOPNOTSUPP;
1760f0068c4aSGarrett Wollman 
1761f0068c4aSGarrett Wollman 	if (ip_rsvpd != NULL)
1762f0068c4aSGarrett Wollman 	  return EADDRINUSE;
1763f0068c4aSGarrett Wollman 
1764f0068c4aSGarrett Wollman 	ip_rsvpd = so;
17651c5de19aSGarrett Wollman 	/*
17661c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-increment
17671c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
17681c5de19aSGarrett Wollman 	 */
17691c5de19aSGarrett Wollman 	if (!ip_rsvp_on) {
17701c5de19aSGarrett Wollman 		ip_rsvp_on = 1;
17711c5de19aSGarrett Wollman 		rsvp_on++;
17721c5de19aSGarrett Wollman 	}
1773f0068c4aSGarrett Wollman 
1774f0068c4aSGarrett Wollman 	return 0;
1775f0068c4aSGarrett Wollman }
1776f0068c4aSGarrett Wollman 
1777f0068c4aSGarrett Wollman int
1778f0068c4aSGarrett Wollman ip_rsvp_done(void)
1779f0068c4aSGarrett Wollman {
1780f0068c4aSGarrett Wollman 	ip_rsvpd = NULL;
17811c5de19aSGarrett Wollman 	/*
17821c5de19aSGarrett Wollman 	 * This may seem silly, but we need to be sure we don't over-decrement
17831c5de19aSGarrett Wollman 	 * the RSVP counter, in case something slips up.
17841c5de19aSGarrett Wollman 	 */
17851c5de19aSGarrett Wollman 	if (ip_rsvp_on) {
17861c5de19aSGarrett Wollman 		ip_rsvp_on = 0;
17871c5de19aSGarrett Wollman 		rsvp_on--;
17881c5de19aSGarrett Wollman 	}
1789f0068c4aSGarrett Wollman 	return 0;
1790f0068c4aSGarrett Wollman }
1791