xref: /freebsd/sys/netinet/ip_icmp.c (revision 09f81a46a58daaafc59a7ea5907502e3f2ae3cd0)
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_icmp.c	8.2 (Berkeley) 1/4/94
34c3aac50fSPeter Wemm  * $FreeBSD$
35df8bae1dSRodney W. Grimes  */
36df8bae1dSRodney W. Grimes 
376a800098SYoshinobu Inoue #include "opt_ipsec.h"
386a800098SYoshinobu Inoue 
39df8bae1dSRodney W. Grimes #include <sys/param.h>
40df8bae1dSRodney W. Grimes #include <sys/systm.h>
41df8bae1dSRodney W. Grimes #include <sys/mbuf.h>
42df8bae1dSRodney W. Grimes #include <sys/protosw.h>
43df8bae1dSRodney W. Grimes #include <sys/socket.h>
44df8bae1dSRodney W. Grimes #include <sys/time.h>
45df8bae1dSRodney W. Grimes #include <sys/kernel.h>
46b5e8ce9fSBruce Evans #include <sys/sysctl.h>
47df8bae1dSRodney W. Grimes 
48df8bae1dSRodney W. Grimes #include <net/if.h>
49df8bae1dSRodney W. Grimes #include <net/route.h>
50df8bae1dSRodney W. Grimes 
515e2d0696SGarrett Wollman #define _IP_VHL
52df8bae1dSRodney W. Grimes #include <netinet/in.h>
53df8bae1dSRodney W. Grimes #include <netinet/in_systm.h>
54df8bae1dSRodney W. Grimes #include <netinet/in_var.h>
55df8bae1dSRodney W. Grimes #include <netinet/ip.h>
56df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h>
57b5e8ce9fSBruce Evans #include <netinet/ip_var.h>
58df8bae1dSRodney W. Grimes #include <netinet/icmp_var.h>
59df8bae1dSRodney W. Grimes 
606a800098SYoshinobu Inoue #ifdef IPSEC
616a800098SYoshinobu Inoue #include <netinet6/ipsec.h>
626a800098SYoshinobu Inoue #include <netkey/key.h>
636a800098SYoshinobu Inoue #endif
646a800098SYoshinobu Inoue 
656a800098SYoshinobu Inoue #include "faith.h"
666a800098SYoshinobu Inoue #if defined(NFAITH) && NFAITH > 0
676a800098SYoshinobu Inoue #include <net/if_types.h>
686a800098SYoshinobu Inoue #endif
696a800098SYoshinobu Inoue 
7072a52a35SJonathan Lemon #include <machine/in_cksum.h>
7172a52a35SJonathan Lemon 
72df8bae1dSRodney W. Grimes /*
73df8bae1dSRodney W. Grimes  * ICMP routines: error generation, receive packet processing, and
74df8bae1dSRodney W. Grimes  * routines to turnaround packets back to the originator, and
75df8bae1dSRodney W. Grimes  * host table maintenance routines.
76df8bae1dSRodney W. Grimes  */
77df8bae1dSRodney W. Grimes 
78f708ef1bSPoul-Henning Kamp static struct	icmpstat icmpstat;
790312fbe9SPoul-Henning Kamp SYSCTL_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RD,
800312fbe9SPoul-Henning Kamp 	&icmpstat, icmpstat, "");
810312fbe9SPoul-Henning Kamp 
820312fbe9SPoul-Henning Kamp static int	icmpmaskrepl = 0;
830312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW,
840312fbe9SPoul-Henning Kamp 	&icmpmaskrepl, 0, "");
850312fbe9SPoul-Henning Kamp 
8618d3153eSDag-Erling Smørgrav static int	drop_redirect = 0;
8718d3153eSDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_RW,
8818d3153eSDag-Erling Smørgrav 	&drop_redirect, 0, "");
8918d3153eSDag-Erling Smørgrav 
906c3b5f69SDag-Erling Smørgrav static int	log_redirect = 0;
916c3b5f69SDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW,
926c3b5f69SDag-Erling Smørgrav 	&log_redirect, 0, "");
936c3b5f69SDag-Erling Smørgrav 
94173c0f9fSWarner Losh static int      icmplim = 200;
9551508de1SMatthew Dillon SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW,
9651508de1SMatthew Dillon 	&icmplim, 0, "");
975fce7fc4SMatthew Dillon 
984f14ee00SDan Moschuk static int	icmplim_output = 1;
994f14ee00SDan Moschuk SYSCTL_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_RW,
1004f14ee00SDan Moschuk 	&icmplim_output, 0, "");
10151508de1SMatthew Dillon 
1025fce7fc4SMatthew Dillon /*
1035fce7fc4SMatthew Dillon  * ICMP broadcast echo sysctl
1045fce7fc4SMatthew Dillon  */
1055fce7fc4SMatthew Dillon 
10661a4defdSJoseph Koshy static int	icmpbmcastecho = 0;
10718d3153eSDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_RW,
10818d3153eSDag-Erling Smørgrav 	&icmpbmcastecho, 0, "");
1097022ea0aSGarrett Wollman 
11051508de1SMatthew Dillon 
111df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
112df8bae1dSRodney W. Grimes int	icmpprintfs = 0;
113df8bae1dSRodney W. Grimes #endif
114df8bae1dSRodney W. Grimes 
1150312fbe9SPoul-Henning Kamp static void	icmp_reflect __P((struct mbuf *));
1160312fbe9SPoul-Henning Kamp static void	icmp_send __P((struct mbuf *, struct mbuf *));
117f708ef1bSPoul-Henning Kamp static int	ip_next_mtu __P((int, int));
1180312fbe9SPoul-Henning Kamp 
119df8bae1dSRodney W. Grimes extern	struct protosw inetsw[];
120df8bae1dSRodney W. Grimes 
121df8bae1dSRodney W. Grimes /*
122df8bae1dSRodney W. Grimes  * Generate an error packet of type error
123df8bae1dSRodney W. Grimes  * in response to bad packet ip.
124df8bae1dSRodney W. Grimes  */
125df8bae1dSRodney W. Grimes void
126df8bae1dSRodney W. Grimes icmp_error(n, type, code, dest, destifp)
127df8bae1dSRodney W. Grimes 	struct mbuf *n;
128df8bae1dSRodney W. Grimes 	int type, code;
129df8bae1dSRodney W. Grimes 	n_long dest;
130df8bae1dSRodney W. Grimes 	struct ifnet *destifp;
131df8bae1dSRodney W. Grimes {
132df8bae1dSRodney W. Grimes 	register struct ip *oip = mtod(n, struct ip *), *nip;
1335e2d0696SGarrett Wollman 	register unsigned oiplen = IP_VHL_HL(oip->ip_vhl) << 2;
134df8bae1dSRodney W. Grimes 	register struct icmp *icp;
135df8bae1dSRodney W. Grimes 	register struct mbuf *m;
136df8bae1dSRodney W. Grimes 	unsigned icmplen;
137df8bae1dSRodney W. Grimes 
138df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
139df8bae1dSRodney W. Grimes 	if (icmpprintfs)
140623ae52eSPoul-Henning Kamp 		printf("icmp_error(%p, %x, %d)\n", oip, type, code);
141df8bae1dSRodney W. Grimes #endif
142df8bae1dSRodney W. Grimes 	if (type != ICMP_REDIRECT)
143df8bae1dSRodney W. Grimes 		icmpstat.icps_error++;
144df8bae1dSRodney W. Grimes 	/*
145df8bae1dSRodney W. Grimes 	 * Don't send error if not the first fragment of message.
146df8bae1dSRodney W. Grimes 	 * Don't error if the old packet protocol was ICMP
147df8bae1dSRodney W. Grimes 	 * error message, only known informational types.
148df8bae1dSRodney W. Grimes 	 */
149df8bae1dSRodney W. Grimes 	if (oip->ip_off &~ (IP_MF|IP_DF))
150df8bae1dSRodney W. Grimes 		goto freeit;
151df8bae1dSRodney W. Grimes 	if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
152df8bae1dSRodney W. Grimes 	  n->m_len >= oiplen + ICMP_MINLEN &&
153df8bae1dSRodney W. Grimes 	  !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
154df8bae1dSRodney W. Grimes 		icmpstat.icps_oldicmp++;
155df8bae1dSRodney W. Grimes 		goto freeit;
156df8bae1dSRodney W. Grimes 	}
157df8bae1dSRodney W. Grimes 	/* Don't send error in response to a multicast or broadcast packet */
158df8bae1dSRodney W. Grimes 	if (n->m_flags & (M_BCAST|M_MCAST))
159df8bae1dSRodney W. Grimes 		goto freeit;
160df8bae1dSRodney W. Grimes 	/*
161df8bae1dSRodney W. Grimes 	 * First, formulate icmp message
162df8bae1dSRodney W. Grimes 	 */
163df8bae1dSRodney W. Grimes 	m = m_gethdr(M_DONTWAIT, MT_HEADER);
164df8bae1dSRodney W. Grimes 	if (m == NULL)
165df8bae1dSRodney W. Grimes 		goto freeit;
1661d027522SRuslan Ermilov 	icmplen = min(oiplen + 8, oip->ip_len);
167df8bae1dSRodney W. Grimes 	m->m_len = icmplen + ICMP_MINLEN;
168df8bae1dSRodney W. Grimes 	MH_ALIGN(m, m->m_len);
169df8bae1dSRodney W. Grimes 	icp = mtod(m, struct icmp *);
170df8bae1dSRodney W. Grimes 	if ((u_int)type > ICMP_MAXTYPE)
171df8bae1dSRodney W. Grimes 		panic("icmp_error");
172df8bae1dSRodney W. Grimes 	icmpstat.icps_outhist[type]++;
173df8bae1dSRodney W. Grimes 	icp->icmp_type = type;
174df8bae1dSRodney W. Grimes 	if (type == ICMP_REDIRECT)
175df8bae1dSRodney W. Grimes 		icp->icmp_gwaddr.s_addr = dest;
176df8bae1dSRodney W. Grimes 	else {
177df8bae1dSRodney W. Grimes 		icp->icmp_void = 0;
178df8bae1dSRodney W. Grimes 		/*
179df8bae1dSRodney W. Grimes 		 * The following assignments assume an overlay with the
180df8bae1dSRodney W. Grimes 		 * zeroed icmp_void field.
181df8bae1dSRodney W. Grimes 		 */
182df8bae1dSRodney W. Grimes 		if (type == ICMP_PARAMPROB) {
183df8bae1dSRodney W. Grimes 			icp->icmp_pptr = code;
184df8bae1dSRodney W. Grimes 			code = 0;
185df8bae1dSRodney W. Grimes 		} else if (type == ICMP_UNREACH &&
186df8bae1dSRodney W. Grimes 			code == ICMP_UNREACH_NEEDFRAG && destifp) {
187df8bae1dSRodney W. Grimes 			icp->icmp_nextmtu = htons(destifp->if_mtu);
188df8bae1dSRodney W. Grimes 		}
189df8bae1dSRodney W. Grimes 	}
190df8bae1dSRodney W. Grimes 
191df8bae1dSRodney W. Grimes 	icp->icmp_code = code;
192df8bae1dSRodney W. Grimes 	bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
193df8bae1dSRodney W. Grimes 	nip = &icp->icmp_ip;
19404287599SRuslan Ermilov 
19504287599SRuslan Ermilov 	/*
19604287599SRuslan Ermilov 	 * Convert fields to network representation.
19704287599SRuslan Ermilov 	 */
19804287599SRuslan Ermilov 	HTONS(nip->ip_len);
19904287599SRuslan Ermilov 	HTONS(nip->ip_off);
200df8bae1dSRodney W. Grimes 
201df8bae1dSRodney W. Grimes 	/*
202df8bae1dSRodney W. Grimes 	 * Now, copy old ip header (without options)
203df8bae1dSRodney W. Grimes 	 * in front of icmp message.
204df8bae1dSRodney W. Grimes 	 */
205df8bae1dSRodney W. Grimes 	if (m->m_data - sizeof(struct ip) < m->m_pktdat)
206df8bae1dSRodney W. Grimes 		panic("icmp len");
207df8bae1dSRodney W. Grimes 	m->m_data -= sizeof(struct ip);
208df8bae1dSRodney W. Grimes 	m->m_len += sizeof(struct ip);
209df8bae1dSRodney W. Grimes 	m->m_pkthdr.len = m->m_len;
210df8bae1dSRodney W. Grimes 	m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
211df8bae1dSRodney W. Grimes 	nip = mtod(m, struct ip *);
212df8bae1dSRodney W. Grimes 	bcopy((caddr_t)oip, (caddr_t)nip, sizeof(struct ip));
213df8bae1dSRodney W. Grimes 	nip->ip_len = m->m_len;
2145e2d0696SGarrett Wollman 	nip->ip_vhl = IP_VHL_BORING;
215df8bae1dSRodney W. Grimes 	nip->ip_p = IPPROTO_ICMP;
216df8bae1dSRodney W. Grimes 	nip->ip_tos = 0;
217df8bae1dSRodney W. Grimes 	icmp_reflect(m);
218df8bae1dSRodney W. Grimes 
219df8bae1dSRodney W. Grimes freeit:
220df8bae1dSRodney W. Grimes 	m_freem(n);
221df8bae1dSRodney W. Grimes }
222df8bae1dSRodney W. Grimes 
223df8bae1dSRodney W. Grimes static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
224df8bae1dSRodney W. Grimes static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
225df8bae1dSRodney W. Grimes static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
226df8bae1dSRodney W. Grimes 
227df8bae1dSRodney W. Grimes /*
228df8bae1dSRodney W. Grimes  * Process a received ICMP message.
229df8bae1dSRodney W. Grimes  */
230df8bae1dSRodney W. Grimes void
2316a800098SYoshinobu Inoue icmp_input(m, off, proto)
232df8bae1dSRodney W. Grimes 	register struct mbuf *m;
2336a800098SYoshinobu Inoue 	int off, proto;
234df8bae1dSRodney W. Grimes {
2356a800098SYoshinobu Inoue 	int hlen = off;
236df8bae1dSRodney W. Grimes 	register struct icmp *icp;
237df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
238df8bae1dSRodney W. Grimes 	int icmplen = ip->ip_len;
239df8bae1dSRodney W. Grimes 	register int i;
240df8bae1dSRodney W. Grimes 	struct in_ifaddr *ia;
241b62d102cSBruce Evans 	void (*ctlfunc) __P((int, struct sockaddr *, void *));
242df8bae1dSRodney W. Grimes 	int code;
243df8bae1dSRodney W. Grimes 
244df8bae1dSRodney W. Grimes 	/*
245df8bae1dSRodney W. Grimes 	 * Locate icmp structure in mbuf, and check
246df8bae1dSRodney W. Grimes 	 * that not corrupted and of at least minimum length.
247df8bae1dSRodney W. Grimes 	 */
248df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
2492b758395SGarrett Wollman 	if (icmpprintfs) {
2502b758395SGarrett Wollman 		char buf[4 * sizeof "123"];
2512b758395SGarrett Wollman 		strcpy(buf, inet_ntoa(ip->ip_src));
2522b758395SGarrett Wollman 		printf("icmp_input from %s to %s, len %d\n",
2532b758395SGarrett Wollman 		       buf, inet_ntoa(ip->ip_dst), icmplen);
2542b758395SGarrett Wollman 	}
255df8bae1dSRodney W. Grimes #endif
256df8bae1dSRodney W. Grimes 	if (icmplen < ICMP_MINLEN) {
257df8bae1dSRodney W. Grimes 		icmpstat.icps_tooshort++;
258df8bae1dSRodney W. Grimes 		goto freeit;
259df8bae1dSRodney W. Grimes 	}
260df8bae1dSRodney W. Grimes 	i = hlen + min(icmplen, ICMP_ADVLENMIN);
261df8bae1dSRodney W. Grimes 	if (m->m_len < i && (m = m_pullup(m, i)) == 0)  {
262df8bae1dSRodney W. Grimes 		icmpstat.icps_tooshort++;
263df8bae1dSRodney W. Grimes 		return;
264df8bae1dSRodney W. Grimes 	}
265df8bae1dSRodney W. Grimes 	ip = mtod(m, struct ip *);
266df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
267df8bae1dSRodney W. Grimes 	m->m_data += hlen;
268df8bae1dSRodney W. Grimes 	icp = mtod(m, struct icmp *);
269df8bae1dSRodney W. Grimes 	if (in_cksum(m, icmplen)) {
270df8bae1dSRodney W. Grimes 		icmpstat.icps_checksum++;
271df8bae1dSRodney W. Grimes 		goto freeit;
272df8bae1dSRodney W. Grimes 	}
273df8bae1dSRodney W. Grimes 	m->m_len += hlen;
274df8bae1dSRodney W. Grimes 	m->m_data -= hlen;
275df8bae1dSRodney W. Grimes 
2766a800098SYoshinobu Inoue #if defined(NFAITH) && 0 < NFAITH
2776a800098SYoshinobu Inoue 	if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
2786a800098SYoshinobu Inoue 		/*
2796a800098SYoshinobu Inoue 		 * Deliver very specific ICMP type only.
2806a800098SYoshinobu Inoue 		 */
2816a800098SYoshinobu Inoue 		switch (icp->icmp_type) {
2826a800098SYoshinobu Inoue 		case ICMP_UNREACH:
2836a800098SYoshinobu Inoue 		case ICMP_TIMXCEED:
2846a800098SYoshinobu Inoue 			break;
2856a800098SYoshinobu Inoue 		default:
2866a800098SYoshinobu Inoue 			goto freeit;
2876a800098SYoshinobu Inoue 		}
2886a800098SYoshinobu Inoue 	}
2896a800098SYoshinobu Inoue #endif
2906a800098SYoshinobu Inoue 
291df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
292df8bae1dSRodney W. Grimes 	if (icmpprintfs)
293df8bae1dSRodney W. Grimes 		printf("icmp_input, type %d code %d\n", icp->icmp_type,
294df8bae1dSRodney W. Grimes 		    icp->icmp_code);
295df8bae1dSRodney W. Grimes #endif
2965b7ee6edSGarrett Wollman 
2976a800098SYoshinobu Inoue #ifdef IPSEC
2986a800098SYoshinobu Inoue 	/* drop it if it does not match the policy */
2996a800098SYoshinobu Inoue 	/* XXX Is there meaning of check in here ? */
3006a800098SYoshinobu Inoue 	if (ipsec4_in_reject(m, NULL)) {
3016a800098SYoshinobu Inoue 		ipsecstat.in_polvio++;
3026a800098SYoshinobu Inoue 		goto freeit;
3036a800098SYoshinobu Inoue 	}
3046a800098SYoshinobu Inoue #endif
3056a800098SYoshinobu Inoue 
3065b7ee6edSGarrett Wollman 	/*
3075b7ee6edSGarrett Wollman 	 * Message type specific processing.
3085b7ee6edSGarrett Wollman 	 */
309df8bae1dSRodney W. Grimes 	if (icp->icmp_type > ICMP_MAXTYPE)
310df8bae1dSRodney W. Grimes 		goto raw;
311df8bae1dSRodney W. Grimes 	icmpstat.icps_inhist[icp->icmp_type]++;
312df8bae1dSRodney W. Grimes 	code = icp->icmp_code;
313df8bae1dSRodney W. Grimes 	switch (icp->icmp_type) {
314df8bae1dSRodney W. Grimes 
315df8bae1dSRodney W. Grimes 	case ICMP_UNREACH:
316df8bae1dSRodney W. Grimes 		switch (code) {
317df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_NET:
318df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_HOST:
319df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_PROTOCOL:
320df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_PORT:
321df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_SRCFAIL:
322df8bae1dSRodney W. Grimes 				code += PRC_UNREACH_NET;
323df8bae1dSRodney W. Grimes 				break;
324df8bae1dSRodney W. Grimes 
325df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_NEEDFRAG:
326df8bae1dSRodney W. Grimes 				code = PRC_MSGSIZE;
327df8bae1dSRodney W. Grimes 				break;
328df8bae1dSRodney W. Grimes 
329df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_NET_UNKNOWN:
330df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_NET_PROHIB:
331df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_TOSNET:
332df8bae1dSRodney W. Grimes 				code = PRC_UNREACH_NET;
333df8bae1dSRodney W. Grimes 				break;
334df8bae1dSRodney W. Grimes 
335df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_HOST_UNKNOWN:
336df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_ISOLATED:
337df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_HOST_PROHIB:
338df8bae1dSRodney W. Grimes 			case ICMP_UNREACH_TOSHOST:
339df8bae1dSRodney W. Grimes 				code = PRC_UNREACH_HOST;
340df8bae1dSRodney W. Grimes 				break;
341df8bae1dSRodney W. Grimes 
3429c4b2574SPaul Traina 			case ICMP_UNREACH_FILTER_PROHIB:
3439c4b2574SPaul Traina 			case ICMP_UNREACH_HOST_PRECEDENCE:
3449c4b2574SPaul Traina 			case ICMP_UNREACH_PRECEDENCE_CUTOFF:
3459c4b2574SPaul Traina 				code = PRC_UNREACH_PORT;
3469c4b2574SPaul Traina 				break;
3479c4b2574SPaul Traina 
348df8bae1dSRodney W. Grimes 			default:
349df8bae1dSRodney W. Grimes 				goto badcode;
350df8bae1dSRodney W. Grimes 		}
351df8bae1dSRodney W. Grimes 		goto deliver;
352df8bae1dSRodney W. Grimes 
353df8bae1dSRodney W. Grimes 	case ICMP_TIMXCEED:
354df8bae1dSRodney W. Grimes 		if (code > 1)
355df8bae1dSRodney W. Grimes 			goto badcode;
356df8bae1dSRodney W. Grimes 		code += PRC_TIMXCEED_INTRANS;
357df8bae1dSRodney W. Grimes 		goto deliver;
358df8bae1dSRodney W. Grimes 
359df8bae1dSRodney W. Grimes 	case ICMP_PARAMPROB:
360df8bae1dSRodney W. Grimes 		if (code > 1)
361df8bae1dSRodney W. Grimes 			goto badcode;
362df8bae1dSRodney W. Grimes 		code = PRC_PARAMPROB;
363df8bae1dSRodney W. Grimes 		goto deliver;
364df8bae1dSRodney W. Grimes 
365df8bae1dSRodney W. Grimes 	case ICMP_SOURCEQUENCH:
366df8bae1dSRodney W. Grimes 		if (code)
367df8bae1dSRodney W. Grimes 			goto badcode;
368df8bae1dSRodney W. Grimes 		code = PRC_QUENCH;
369df8bae1dSRodney W. Grimes 	deliver:
370df8bae1dSRodney W. Grimes 		/*
371df8bae1dSRodney W. Grimes 		 * Problem with datagram; advise higher level routines.
372df8bae1dSRodney W. Grimes 		 */
373df8bae1dSRodney W. Grimes 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
3745e2d0696SGarrett Wollman 		    IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) {
375df8bae1dSRodney W. Grimes 			icmpstat.icps_badlen++;
376df8bae1dSRodney W. Grimes 			goto freeit;
377df8bae1dSRodney W. Grimes 		}
378df8bae1dSRodney W. Grimes 		NTOHS(icp->icmp_ip.ip_len);
3795b7ee6edSGarrett Wollman 		/* Discard ICMP's in response to multicast packets */
3805b7ee6edSGarrett Wollman 		if (IN_MULTICAST(ntohl(icp->icmp_ip.ip_dst.s_addr)))
3815b7ee6edSGarrett Wollman 			goto badcode;
382df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
383df8bae1dSRodney W. Grimes 		if (icmpprintfs)
384df8bae1dSRodney W. Grimes 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
385df8bae1dSRodney W. Grimes #endif
386df8bae1dSRodney W. Grimes 		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
387b7a44e34SGarrett Wollman #if 1
3885cbf3e08SGarrett Wollman 		/*
3895cbf3e08SGarrett Wollman 		 * MTU discovery:
3905cbf3e08SGarrett Wollman 		 * If we got a needfrag and there is a host route to the
3915cbf3e08SGarrett Wollman 		 * original destination, and the MTU is not locked, then
3925cbf3e08SGarrett Wollman 		 * set the MTU in the route to the suggested new value
3935cbf3e08SGarrett Wollman 		 * (if given) and then notify as usual.  The ULPs will
3945cbf3e08SGarrett Wollman 		 * notice that the MTU has changed and adapt accordingly.
3955cbf3e08SGarrett Wollman 		 * If no new MTU was suggested, then we guess a new one
3965cbf3e08SGarrett Wollman 		 * less than the current value.  If the new MTU is
3975cbf3e08SGarrett Wollman 		 * unreasonably small (arbitrarily set at 296), then
3985cbf3e08SGarrett Wollman 		 * we reset the MTU to the interface value and enable the
3995cbf3e08SGarrett Wollman 		 * lock bit, indicating that we are no longer doing MTU
4005cbf3e08SGarrett Wollman 		 * discovery.
4015cbf3e08SGarrett Wollman 		 */
4025cbf3e08SGarrett Wollman 		if (code == PRC_MSGSIZE) {
4035cbf3e08SGarrett Wollman 			struct rtentry *rt;
4045cbf3e08SGarrett Wollman 			int mtu;
4055cbf3e08SGarrett Wollman 
4065cbf3e08SGarrett Wollman 			rt = rtalloc1((struct sockaddr *)&icmpsrc, 0,
4075cbf3e08SGarrett Wollman 				      RTF_CLONING | RTF_PRCLONING);
4085cbf3e08SGarrett Wollman 			if (rt && (rt->rt_flags & RTF_HOST)
4095cbf3e08SGarrett Wollman 			    && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
4105cbf3e08SGarrett Wollman 				mtu = ntohs(icp->icmp_nextmtu);
4115cbf3e08SGarrett Wollman 				if (!mtu)
4125cbf3e08SGarrett Wollman 					mtu = ip_next_mtu(rt->rt_rmx.rmx_mtu,
4135cbf3e08SGarrett Wollman 							  1);
414be070f43SGarrett Wollman #ifdef DEBUG_MTUDISC
415be070f43SGarrett Wollman 				printf("MTU for %s reduced to %d\n",
416be070f43SGarrett Wollman 					inet_ntoa(icmpsrc.sin_addr), mtu);
417be070f43SGarrett Wollman #endif
418be070f43SGarrett Wollman 				if (mtu < 296) {
419b7a44e34SGarrett Wollman 					/* rt->rt_rmx.rmx_mtu =
420b7a44e34SGarrett Wollman 						rt->rt_ifp->if_mtu; */
4215cbf3e08SGarrett Wollman 					rt->rt_rmx.rmx_locks |= RTV_MTU;
4225cbf3e08SGarrett Wollman 				} else if (rt->rt_rmx.rmx_mtu > mtu) {
4235cbf3e08SGarrett Wollman 					rt->rt_rmx.rmx_mtu = mtu;
4245cbf3e08SGarrett Wollman 				}
4255cbf3e08SGarrett Wollman 			}
4265cbf3e08SGarrett Wollman 			if (rt)
4275cbf3e08SGarrett Wollman 				RTFREE(rt);
4285cbf3e08SGarrett Wollman 		}
4295cbf3e08SGarrett Wollman 
430b7a44e34SGarrett Wollman #endif
4316a800098SYoshinobu Inoue 		/*
4326a800098SYoshinobu Inoue 		 * XXX if the packet contains [IPv4 AH TCP], we can't make a
4336a800098SYoshinobu Inoue 		 * notification to TCP layer.
4346a800098SYoshinobu Inoue 		 */
435623ae52eSPoul-Henning Kamp 		ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput;
436623ae52eSPoul-Henning Kamp 		if (ctlfunc)
437df8bae1dSRodney W. Grimes 			(*ctlfunc)(code, (struct sockaddr *)&icmpsrc,
438b62d102cSBruce Evans 				   (void *)&icp->icmp_ip);
439df8bae1dSRodney W. Grimes 		break;
440df8bae1dSRodney W. Grimes 
441df8bae1dSRodney W. Grimes 	badcode:
442df8bae1dSRodney W. Grimes 		icmpstat.icps_badcode++;
443df8bae1dSRodney W. Grimes 		break;
444df8bae1dSRodney W. Grimes 
445df8bae1dSRodney W. Grimes 	case ICMP_ECHO:
4467022ea0aSGarrett Wollman 		if (!icmpbmcastecho
447d311884fSDavid Greenman 		    && (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
4487022ea0aSGarrett Wollman 			icmpstat.icps_bmcastecho++;
4497022ea0aSGarrett Wollman 			break;
4507022ea0aSGarrett Wollman 		}
451df8bae1dSRodney W. Grimes 		icp->icmp_type = ICMP_ECHOREPLY;
45209f81a46SBosko Milekic 		if (badport_bandlim(BANDLIM_ECHO) < 0)
45309f81a46SBosko Milekic 			goto freeit;
45409f81a46SBosko Milekic 		else
455df8bae1dSRodney W. Grimes 			goto reflect;
456df8bae1dSRodney W. Grimes 
457df8bae1dSRodney W. Grimes 	case ICMP_TSTAMP:
458fe0fb8abSGarrett Wollman 		if (!icmpbmcastecho
459d311884fSDavid Greenman 		    && (m->m_flags & (M_MCAST | M_BCAST)) != 0) {
460fe0fb8abSGarrett Wollman 			icmpstat.icps_bmcasttstamp++;
461fe0fb8abSGarrett Wollman 			break;
462fe0fb8abSGarrett Wollman 		}
463df8bae1dSRodney W. Grimes 		if (icmplen < ICMP_TSLEN) {
464df8bae1dSRodney W. Grimes 			icmpstat.icps_badlen++;
465df8bae1dSRodney W. Grimes 			break;
466df8bae1dSRodney W. Grimes 		}
467df8bae1dSRodney W. Grimes 		icp->icmp_type = ICMP_TSTAMPREPLY;
468df8bae1dSRodney W. Grimes 		icp->icmp_rtime = iptime();
469df8bae1dSRodney W. Grimes 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
47009f81a46SBosko Milekic 		if (badport_bandlim(BANDLIM_TSTAMP) < 0)
47109f81a46SBosko Milekic 			goto freeit;
47209f81a46SBosko Milekic 		else
473df8bae1dSRodney W. Grimes 			goto reflect;
474df8bae1dSRodney W. Grimes 
475df8bae1dSRodney W. Grimes 	case ICMP_MASKREQ:
476df8bae1dSRodney W. Grimes #define	satosin(sa)	((struct sockaddr_in *)(sa))
477df8bae1dSRodney W. Grimes 		if (icmpmaskrepl == 0)
478df8bae1dSRodney W. Grimes 			break;
479df8bae1dSRodney W. Grimes 		/*
480df8bae1dSRodney W. Grimes 		 * We are not able to respond with all ones broadcast
481df8bae1dSRodney W. Grimes 		 * unless we receive it over a point-to-point interface.
482df8bae1dSRodney W. Grimes 		 */
483df8bae1dSRodney W. Grimes 		if (icmplen < ICMP_MASKLEN)
484df8bae1dSRodney W. Grimes 			break;
485df8bae1dSRodney W. Grimes 		switch (ip->ip_dst.s_addr) {
486df8bae1dSRodney W. Grimes 
487df8bae1dSRodney W. Grimes 		case INADDR_BROADCAST:
488df8bae1dSRodney W. Grimes 		case INADDR_ANY:
489df8bae1dSRodney W. Grimes 			icmpdst.sin_addr = ip->ip_src;
490df8bae1dSRodney W. Grimes 			break;
491df8bae1dSRodney W. Grimes 
492df8bae1dSRodney W. Grimes 		default:
493df8bae1dSRodney W. Grimes 			icmpdst.sin_addr = ip->ip_dst;
494df8bae1dSRodney W. Grimes 		}
495df8bae1dSRodney W. Grimes 		ia = (struct in_ifaddr *)ifaof_ifpforaddr(
496df8bae1dSRodney W. Grimes 			    (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
497df8bae1dSRodney W. Grimes 		if (ia == 0)
498df8bae1dSRodney W. Grimes 			break;
4997e6f7714SPoul-Henning Kamp 		if (ia->ia_ifp == 0)
5007e6f7714SPoul-Henning Kamp 			break;
501df8bae1dSRodney W. Grimes 		icp->icmp_type = ICMP_MASKREPLY;
502df8bae1dSRodney W. Grimes 		icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
503df8bae1dSRodney W. Grimes 		if (ip->ip_src.s_addr == 0) {
504df8bae1dSRodney W. Grimes 			if (ia->ia_ifp->if_flags & IFF_BROADCAST)
505df8bae1dSRodney W. Grimes 			    ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
506df8bae1dSRodney W. Grimes 			else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
507df8bae1dSRodney W. Grimes 			    ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
508df8bae1dSRodney W. Grimes 		}
509df8bae1dSRodney W. Grimes reflect:
510df8bae1dSRodney W. Grimes 		ip->ip_len += hlen;	/* since ip_input deducts this */
511df8bae1dSRodney W. Grimes 		icmpstat.icps_reflect++;
512df8bae1dSRodney W. Grimes 		icmpstat.icps_outhist[icp->icmp_type]++;
513df8bae1dSRodney W. Grimes 		icmp_reflect(m);
514df8bae1dSRodney W. Grimes 		return;
515df8bae1dSRodney W. Grimes 
516df8bae1dSRodney W. Grimes 	case ICMP_REDIRECT:
51718d3153eSDag-Erling Smørgrav 		if (log_redirect) {
51818d3153eSDag-Erling Smørgrav 			u_long src, dst, gw;
51918d3153eSDag-Erling Smørgrav 
52018d3153eSDag-Erling Smørgrav 			src = ntohl(ip->ip_src.s_addr);
52118d3153eSDag-Erling Smørgrav 			dst = ntohl(icp->icmp_ip.ip_dst.s_addr);
52218d3153eSDag-Erling Smørgrav 			gw = ntohl(icp->icmp_gwaddr.s_addr);
52318d3153eSDag-Erling Smørgrav 			printf("icmp redirect from %d.%d.%d.%d: "
52418d3153eSDag-Erling Smørgrav 			       "%d.%d.%d.%d => %d.%d.%d.%d\n",
52518d3153eSDag-Erling Smørgrav 			       (int)(src >> 24), (int)((src >> 16) & 0xff),
52618d3153eSDag-Erling Smørgrav 			       (int)((src >> 8) & 0xff), (int)(src & 0xff),
52718d3153eSDag-Erling Smørgrav 			       (int)(dst >> 24), (int)((dst >> 16) & 0xff),
52818d3153eSDag-Erling Smørgrav 			       (int)((dst >> 8) & 0xff), (int)(dst & 0xff),
52918d3153eSDag-Erling Smørgrav 			       (int)(gw >> 24), (int)((gw >> 16) & 0xff),
53018d3153eSDag-Erling Smørgrav 			       (int)((gw >> 8) & 0xff), (int)(gw & 0xff));
53118d3153eSDag-Erling Smørgrav 		}
53218d3153eSDag-Erling Smørgrav 		if (drop_redirect)
53318d3153eSDag-Erling Smørgrav 			break;
534df8bae1dSRodney W. Grimes 		if (code > 3)
535df8bae1dSRodney W. Grimes 			goto badcode;
536df8bae1dSRodney W. Grimes 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
5375e2d0696SGarrett Wollman 		    IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) {
538df8bae1dSRodney W. Grimes 			icmpstat.icps_badlen++;
539df8bae1dSRodney W. Grimes 			break;
540df8bae1dSRodney W. Grimes 		}
541df8bae1dSRodney W. Grimes 		/*
542df8bae1dSRodney W. Grimes 		 * Short circuit routing redirects to force
543df8bae1dSRodney W. Grimes 		 * immediate change in the kernel's routing
544df8bae1dSRodney W. Grimes 		 * tables.  The message is also handed to anyone
545df8bae1dSRodney W. Grimes 		 * listening on a raw socket (e.g. the routing
546df8bae1dSRodney W. Grimes 		 * daemon for use in updating its tables).
547df8bae1dSRodney W. Grimes 		 */
548df8bae1dSRodney W. Grimes 		icmpgw.sin_addr = ip->ip_src;
549df8bae1dSRodney W. Grimes 		icmpdst.sin_addr = icp->icmp_gwaddr;
550df8bae1dSRodney W. Grimes #ifdef	ICMPPRINTFS
5512b758395SGarrett Wollman 		if (icmpprintfs) {
5522b758395SGarrett Wollman 			char buf[4 * sizeof "123"];
5532b758395SGarrett Wollman 			strcpy(buf, inet_ntoa(icp->icmp_ip.ip_dst));
5542b758395SGarrett Wollman 
5552b758395SGarrett Wollman 			printf("redirect dst %s to %s\n",
5562b758395SGarrett Wollman 			       buf, inet_ntoa(icp->icmp_gwaddr));
5572b758395SGarrett Wollman 		}
558df8bae1dSRodney W. Grimes #endif
559df8bae1dSRodney W. Grimes 		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
560df8bae1dSRodney W. Grimes 		rtredirect((struct sockaddr *)&icmpsrc,
561df8bae1dSRodney W. Grimes 		  (struct sockaddr *)&icmpdst,
562df8bae1dSRodney W. Grimes 		  (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
563df8bae1dSRodney W. Grimes 		  (struct sockaddr *)&icmpgw, (struct rtentry **)0);
564df8bae1dSRodney W. Grimes 		pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc);
5656a800098SYoshinobu Inoue #ifdef IPSEC
5666a800098SYoshinobu Inoue 		key_sa_routechange((struct sockaddr *)&icmpsrc);
5676a800098SYoshinobu Inoue #endif
568df8bae1dSRodney W. Grimes 		break;
569df8bae1dSRodney W. Grimes 
570df8bae1dSRodney W. Grimes 	/*
571df8bae1dSRodney W. Grimes 	 * No kernel processing for the following;
572df8bae1dSRodney W. Grimes 	 * just fall through to send to raw listener.
573df8bae1dSRodney W. Grimes 	 */
574df8bae1dSRodney W. Grimes 	case ICMP_ECHOREPLY:
575df8bae1dSRodney W. Grimes 	case ICMP_ROUTERADVERT:
576df8bae1dSRodney W. Grimes 	case ICMP_ROUTERSOLICIT:
577df8bae1dSRodney W. Grimes 	case ICMP_TSTAMPREPLY:
578df8bae1dSRodney W. Grimes 	case ICMP_IREQREPLY:
579df8bae1dSRodney W. Grimes 	case ICMP_MASKREPLY:
580df8bae1dSRodney W. Grimes 	default:
581df8bae1dSRodney W. Grimes 		break;
582df8bae1dSRodney W. Grimes 	}
583df8bae1dSRodney W. Grimes 
584df8bae1dSRodney W. Grimes raw:
5856a800098SYoshinobu Inoue 	rip_input(m, off, proto);
586df8bae1dSRodney W. Grimes 	return;
587df8bae1dSRodney W. Grimes 
588df8bae1dSRodney W. Grimes freeit:
589df8bae1dSRodney W. Grimes 	m_freem(m);
590df8bae1dSRodney W. Grimes }
591df8bae1dSRodney W. Grimes 
592df8bae1dSRodney W. Grimes /*
593df8bae1dSRodney W. Grimes  * Reflect the ip packet back to the source
594df8bae1dSRodney W. Grimes  */
5950312fbe9SPoul-Henning Kamp static void
596df8bae1dSRodney W. Grimes icmp_reflect(m)
597df8bae1dSRodney W. Grimes 	struct mbuf *m;
598df8bae1dSRodney W. Grimes {
599df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
600df8bae1dSRodney W. Grimes 	register struct in_ifaddr *ia;
601df8bae1dSRodney W. Grimes 	struct in_addr t;
602b5e8ce9fSBruce Evans 	struct mbuf *opts = 0;
6035e2d0696SGarrett Wollman 	int optlen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof(struct ip);
604df8bae1dSRodney W. Grimes 
605df8bae1dSRodney W. Grimes 	if (!in_canforward(ip->ip_src) &&
606df8bae1dSRodney W. Grimes 	    ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) !=
607df8bae1dSRodney W. Grimes 	     (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
608df8bae1dSRodney W. Grimes 		m_freem(m);	/* Bad return address */
609df8bae1dSRodney W. Grimes 		goto done;	/* Ip_output() will check for broadcast */
610df8bae1dSRodney W. Grimes 	}
611df8bae1dSRodney W. Grimes 	t = ip->ip_dst;
612df8bae1dSRodney W. Grimes 	ip->ip_dst = ip->ip_src;
613df8bae1dSRodney W. Grimes 	/*
614df8bae1dSRodney W. Grimes 	 * If the incoming packet was addressed directly to us,
615df8bae1dSRodney W. Grimes 	 * use dst as the src for the reply.  Otherwise (broadcast
616df8bae1dSRodney W. Grimes 	 * or anonymous), use the address which corresponds
617df8bae1dSRodney W. Grimes 	 * to the incoming interface.
618df8bae1dSRodney W. Grimes 	 */
61959562606SGarrett Wollman 	for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
620df8bae1dSRodney W. Grimes 		if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
621df8bae1dSRodney W. Grimes 			break;
6227e6f7714SPoul-Henning Kamp 		if (ia->ia_ifp && (ia->ia_ifp->if_flags & IFF_BROADCAST) &&
623df8bae1dSRodney W. Grimes 		    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
624df8bae1dSRodney W. Grimes 			break;
625df8bae1dSRodney W. Grimes 	}
626df8bae1dSRodney W. Grimes 	icmpdst.sin_addr = t;
62741fbdc96SJulian Elischer 	if ((ia == (struct in_ifaddr *)0) && m->m_pkthdr.rcvif)
628df8bae1dSRodney W. Grimes 		ia = (struct in_ifaddr *)ifaof_ifpforaddr(
629df8bae1dSRodney W. Grimes 			(struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif);
630df8bae1dSRodney W. Grimes 	/*
631df8bae1dSRodney W. Grimes 	 * The following happens if the packet was not addressed to us,
632df8bae1dSRodney W. Grimes 	 * and was received on an interface with no IP address.
633df8bae1dSRodney W. Grimes 	 */
634df8bae1dSRodney W. Grimes 	if (ia == (struct in_ifaddr *)0)
63559562606SGarrett Wollman 		ia = in_ifaddrhead.tqh_first;
636df8bae1dSRodney W. Grimes 	t = IA_SIN(ia)->sin_addr;
637df8bae1dSRodney W. Grimes 	ip->ip_src = t;
638df8bae1dSRodney W. Grimes 	ip->ip_ttl = MAXTTL;
639df8bae1dSRodney W. Grimes 
640df8bae1dSRodney W. Grimes 	if (optlen > 0) {
641df8bae1dSRodney W. Grimes 		register u_char *cp;
642df8bae1dSRodney W. Grimes 		int opt, cnt;
643df8bae1dSRodney W. Grimes 		u_int len;
644df8bae1dSRodney W. Grimes 
645df8bae1dSRodney W. Grimes 		/*
646df8bae1dSRodney W. Grimes 		 * Retrieve any source routing from the incoming packet;
647df8bae1dSRodney W. Grimes 		 * add on any record-route or timestamp options.
648df8bae1dSRodney W. Grimes 		 */
649df8bae1dSRodney W. Grimes 		cp = (u_char *) (ip + 1);
650df8bae1dSRodney W. Grimes 		if ((opts = ip_srcroute()) == 0 &&
651df8bae1dSRodney W. Grimes 		    (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
652df8bae1dSRodney W. Grimes 			opts->m_len = sizeof(struct in_addr);
653df8bae1dSRodney W. Grimes 			mtod(opts, struct in_addr *)->s_addr = 0;
654df8bae1dSRodney W. Grimes 		}
655df8bae1dSRodney W. Grimes 		if (opts) {
656df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
657df8bae1dSRodney W. Grimes 		    if (icmpprintfs)
658df8bae1dSRodney W. Grimes 			    printf("icmp_reflect optlen %d rt %d => ",
659df8bae1dSRodney W. Grimes 				optlen, opts->m_len);
660df8bae1dSRodney W. Grimes #endif
661df8bae1dSRodney W. Grimes 		    for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
662df8bae1dSRodney W. Grimes 			    opt = cp[IPOPT_OPTVAL];
663df8bae1dSRodney W. Grimes 			    if (opt == IPOPT_EOL)
664df8bae1dSRodney W. Grimes 				    break;
665df8bae1dSRodney W. Grimes 			    if (opt == IPOPT_NOP)
666df8bae1dSRodney W. Grimes 				    len = 1;
667df8bae1dSRodney W. Grimes 			    else {
668707d00a3SJonathan Lemon 				    if (cnt < IPOPT_OLEN + sizeof(*cp))
669707d00a3SJonathan Lemon 					    break;
670df8bae1dSRodney W. Grimes 				    len = cp[IPOPT_OLEN];
671707d00a3SJonathan Lemon 				    if (len < IPOPT_OLEN + sizeof(*cp) ||
672707d00a3SJonathan Lemon 				        len > cnt)
673df8bae1dSRodney W. Grimes 					    break;
674df8bae1dSRodney W. Grimes 			    }
675df8bae1dSRodney W. Grimes 			    /*
676df8bae1dSRodney W. Grimes 			     * Should check for overflow, but it "can't happen"
677df8bae1dSRodney W. Grimes 			     */
678df8bae1dSRodney W. Grimes 			    if (opt == IPOPT_RR || opt == IPOPT_TS ||
679df8bae1dSRodney W. Grimes 				opt == IPOPT_SECURITY) {
680df8bae1dSRodney W. Grimes 				    bcopy((caddr_t)cp,
681df8bae1dSRodney W. Grimes 					mtod(opts, caddr_t) + opts->m_len, len);
682df8bae1dSRodney W. Grimes 				    opts->m_len += len;
683df8bae1dSRodney W. Grimes 			    }
684df8bae1dSRodney W. Grimes 		    }
685df8bae1dSRodney W. Grimes 		    /* Terminate & pad, if necessary */
686623ae52eSPoul-Henning Kamp 		    cnt = opts->m_len % 4;
687623ae52eSPoul-Henning Kamp 		    if (cnt) {
688df8bae1dSRodney W. Grimes 			    for (; cnt < 4; cnt++) {
689df8bae1dSRodney W. Grimes 				    *(mtod(opts, caddr_t) + opts->m_len) =
690df8bae1dSRodney W. Grimes 					IPOPT_EOL;
691df8bae1dSRodney W. Grimes 				    opts->m_len++;
692df8bae1dSRodney W. Grimes 			    }
693df8bae1dSRodney W. Grimes 		    }
694df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
695df8bae1dSRodney W. Grimes 		    if (icmpprintfs)
696df8bae1dSRodney W. Grimes 			    printf("%d\n", opts->m_len);
697df8bae1dSRodney W. Grimes #endif
698df8bae1dSRodney W. Grimes 		}
699df8bae1dSRodney W. Grimes 		/*
700df8bae1dSRodney W. Grimes 		 * Now strip out original options by copying rest of first
701df8bae1dSRodney W. Grimes 		 * mbuf's data back, and adjust the IP length.
702df8bae1dSRodney W. Grimes 		 */
703df8bae1dSRodney W. Grimes 		ip->ip_len -= optlen;
7045e2d0696SGarrett Wollman 		ip->ip_vhl = IP_VHL_BORING;
705df8bae1dSRodney W. Grimes 		m->m_len -= optlen;
706df8bae1dSRodney W. Grimes 		if (m->m_flags & M_PKTHDR)
707df8bae1dSRodney W. Grimes 			m->m_pkthdr.len -= optlen;
708df8bae1dSRodney W. Grimes 		optlen += sizeof(struct ip);
709df8bae1dSRodney W. Grimes 		bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
710df8bae1dSRodney W. Grimes 			 (unsigned)(m->m_len - sizeof(struct ip)));
711df8bae1dSRodney W. Grimes 	}
712df8bae1dSRodney W. Grimes 	m->m_flags &= ~(M_BCAST|M_MCAST);
713df8bae1dSRodney W. Grimes 	icmp_send(m, opts);
714df8bae1dSRodney W. Grimes done:
715df8bae1dSRodney W. Grimes 	if (opts)
716df8bae1dSRodney W. Grimes 		(void)m_free(opts);
717df8bae1dSRodney W. Grimes }
718df8bae1dSRodney W. Grimes 
719df8bae1dSRodney W. Grimes /*
720df8bae1dSRodney W. Grimes  * Send an icmp packet back to the ip level,
721df8bae1dSRodney W. Grimes  * after supplying a checksum.
722df8bae1dSRodney W. Grimes  */
7230312fbe9SPoul-Henning Kamp static void
724df8bae1dSRodney W. Grimes icmp_send(m, opts)
725df8bae1dSRodney W. Grimes 	register struct mbuf *m;
726df8bae1dSRodney W. Grimes 	struct mbuf *opts;
727df8bae1dSRodney W. Grimes {
728df8bae1dSRodney W. Grimes 	register struct ip *ip = mtod(m, struct ip *);
729df8bae1dSRodney W. Grimes 	register int hlen;
730df8bae1dSRodney W. Grimes 	register struct icmp *icp;
731d3d20ad1SGarrett Wollman 	struct route ro;
732df8bae1dSRodney W. Grimes 
7335e2d0696SGarrett Wollman 	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
734df8bae1dSRodney W. Grimes 	m->m_data += hlen;
735df8bae1dSRodney W. Grimes 	m->m_len -= hlen;
736df8bae1dSRodney W. Grimes 	icp = mtod(m, struct icmp *);
737df8bae1dSRodney W. Grimes 	icp->icmp_cksum = 0;
738df8bae1dSRodney W. Grimes 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
739df8bae1dSRodney W. Grimes 	m->m_data -= hlen;
740df8bae1dSRodney W. Grimes 	m->m_len += hlen;
74194446a2eSArchie Cobbs 	m->m_pkthdr.rcvif = (struct ifnet *)0;
742df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS
7432b758395SGarrett Wollman 	if (icmpprintfs) {
7442b758395SGarrett Wollman 		char buf[4 * sizeof "123"];
7452b758395SGarrett Wollman 		strcpy(buf, inet_ntoa(ip->ip_dst));
7462b758395SGarrett Wollman 		printf("icmp_send dst %s src %s\n",
7472b758395SGarrett Wollman 		       buf, inet_ntoa(ip->ip_src));
7482b758395SGarrett Wollman 	}
749df8bae1dSRodney W. Grimes #endif
750d3d20ad1SGarrett Wollman 	bzero(&ro, sizeof ro);
751d3d20ad1SGarrett Wollman 	(void) ip_output(m, opts, &ro, 0, NULL);
752d3d20ad1SGarrett Wollman 	if (ro.ro_rt)
753d3d20ad1SGarrett Wollman 		RTFREE(ro.ro_rt);
754df8bae1dSRodney W. Grimes }
755df8bae1dSRodney W. Grimes 
756df8bae1dSRodney W. Grimes n_time
757df8bae1dSRodney W. Grimes iptime()
758df8bae1dSRodney W. Grimes {
759df8bae1dSRodney W. Grimes 	struct timeval atv;
760df8bae1dSRodney W. Grimes 	u_long t;
761df8bae1dSRodney W. Grimes 
762df8bae1dSRodney W. Grimes 	microtime(&atv);
763df8bae1dSRodney W. Grimes 	t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
764df8bae1dSRodney W. Grimes 	return (htonl(t));
765df8bae1dSRodney W. Grimes }
766df8bae1dSRodney W. Grimes 
767b7a44e34SGarrett Wollman #if 1
7685cbf3e08SGarrett Wollman /*
7695cbf3e08SGarrett Wollman  * Return the next larger or smaller MTU plateau (table from RFC 1191)
7705cbf3e08SGarrett Wollman  * given current value MTU.  If DIR is less than zero, a larger plateau
7715cbf3e08SGarrett Wollman  * is returned; otherwise, a smaller value is returned.
7725cbf3e08SGarrett Wollman  */
773f708ef1bSPoul-Henning Kamp static int
7745cbf3e08SGarrett Wollman ip_next_mtu(mtu, dir)
7755cbf3e08SGarrett Wollman 	int mtu;
7765cbf3e08SGarrett Wollman 	int dir;
7775cbf3e08SGarrett Wollman {
7785cbf3e08SGarrett Wollman 	static int mtutab[] = {
7795cbf3e08SGarrett Wollman 		65535, 32000, 17914, 8166, 4352, 2002, 1492, 1006, 508, 296,
7805cbf3e08SGarrett Wollman 		68, 0
7815cbf3e08SGarrett Wollman 	};
7825cbf3e08SGarrett Wollman 	int i;
7835cbf3e08SGarrett Wollman 
7845cbf3e08SGarrett Wollman 	for (i = 0; i < (sizeof mtutab) / (sizeof mtutab[0]); i++) {
7855cbf3e08SGarrett Wollman 		if (mtu >= mtutab[i])
7865cbf3e08SGarrett Wollman 			break;
7875cbf3e08SGarrett Wollman 	}
7885cbf3e08SGarrett Wollman 
7895cbf3e08SGarrett Wollman 	if (dir < 0) {
7905cbf3e08SGarrett Wollman 		if (i == 0) {
7915cbf3e08SGarrett Wollman 			return 0;
7925cbf3e08SGarrett Wollman 		} else {
7935cbf3e08SGarrett Wollman 			return mtutab[i - 1];
7945cbf3e08SGarrett Wollman 		}
7955cbf3e08SGarrett Wollman 	} else {
7965cbf3e08SGarrett Wollman 		if (mtutab[i] == 0) {
7975cbf3e08SGarrett Wollman 			return 0;
7985cbf3e08SGarrett Wollman 		} else if(mtu > mtutab[i]) {
7995cbf3e08SGarrett Wollman 			return mtutab[i];
8005cbf3e08SGarrett Wollman 		} else {
8015cbf3e08SGarrett Wollman 			return mtutab[i + 1];
8025cbf3e08SGarrett Wollman 		}
8035cbf3e08SGarrett Wollman 	}
8045cbf3e08SGarrett Wollman }
805b7a44e34SGarrett Wollman #endif
80651508de1SMatthew Dillon 
80751508de1SMatthew Dillon 
80851508de1SMatthew Dillon /*
80951508de1SMatthew Dillon  * badport_bandlim() - check for ICMP bandwidth limit
81051508de1SMatthew Dillon  *
81151508de1SMatthew Dillon  *	Return 0 if it is ok to send an ICMP error response, -1 if we have
81251508de1SMatthew Dillon  *	hit our bandwidth limit and it is not ok.
81351508de1SMatthew Dillon  *
81451508de1SMatthew Dillon  *	If icmplim is <= 0, the feature is disabled and 0 is returned.
81551508de1SMatthew Dillon  *
81651508de1SMatthew Dillon  *	For now we separate the TCP and UDP subsystems w/ different 'which'
81751508de1SMatthew Dillon  *	values.  We may eventually remove this separation (and simplify the
81851508de1SMatthew Dillon  *	code further).
81951508de1SMatthew Dillon  *
82051508de1SMatthew Dillon  *	Note that the printing of the error message is delayed so we can
82151508de1SMatthew Dillon  *	properly print the icmp error rate that the system was trying to do
82251508de1SMatthew Dillon  *	(i.e. 22000/100 pps, etc...).  This can cause long delays in printing
82351508de1SMatthew Dillon  *	the 'final' error, but it doesn't make sense to solve the printing
82451508de1SMatthew Dillon  *	delay with more complex code.
82551508de1SMatthew Dillon  */
82651508de1SMatthew Dillon 
82751508de1SMatthew Dillon int
82851508de1SMatthew Dillon badport_bandlim(int which)
82951508de1SMatthew Dillon {
83009f81a46SBosko Milekic 	static int lticks[BANDLIM_MAX + 1];
83109f81a46SBosko Milekic 	static int lpackets[BANDLIM_MAX + 1];
83251508de1SMatthew Dillon 	int dticks;
83309f81a46SBosko Milekic 	const char *bandlimittype[] = {
83409f81a46SBosko Milekic 		"Limiting icmp unreach response",
83509f81a46SBosko Milekic 		"Limiting closed port RST response",
83609f81a46SBosko Milekic 		"Limiting open port RST response",
83709f81a46SBosko Milekic 		"Limiting icmp ping response",
83809f81a46SBosko Milekic 		"Limiting icmp tstamp response"
83909f81a46SBosko Milekic 		};
84051508de1SMatthew Dillon 
84151508de1SMatthew Dillon 	/*
84251508de1SMatthew Dillon 	 * Return ok status if feature disabled or argument out of
84351508de1SMatthew Dillon 	 * ranage.
84451508de1SMatthew Dillon 	 */
84551508de1SMatthew Dillon 
84609f81a46SBosko Milekic 	if (icmplim <= 0 || which > BANDLIM_MAX || which < 0)
84751508de1SMatthew Dillon 		return(0);
84851508de1SMatthew Dillon 	dticks = ticks - lticks[which];
84951508de1SMatthew Dillon 
85051508de1SMatthew Dillon 	/*
85151508de1SMatthew Dillon 	 * reset stats when cumulative dt exceeds one second.
85251508de1SMatthew Dillon 	 */
85351508de1SMatthew Dillon 
85451508de1SMatthew Dillon 	if ((unsigned int)dticks > hz) {
8554f14ee00SDan Moschuk 		if (lpackets[which] > icmplim && icmplim_output) {
85609f81a46SBosko Milekic 			printf("%s from %d to %d packets per second\n",
85709f81a46SBosko Milekic 				bandlimittype[which],
85851508de1SMatthew Dillon 				lpackets[which],
85951508de1SMatthew Dillon 				icmplim
86051508de1SMatthew Dillon 			);
86151508de1SMatthew Dillon 		}
86251508de1SMatthew Dillon 		lticks[which] = ticks;
86351508de1SMatthew Dillon 		lpackets[which] = 0;
86451508de1SMatthew Dillon 	}
86551508de1SMatthew Dillon 
86651508de1SMatthew Dillon 	/*
86751508de1SMatthew Dillon 	 * bump packet count
86851508de1SMatthew Dillon 	 */
86951508de1SMatthew Dillon 
87051508de1SMatthew Dillon 	if (++lpackets[which] > icmplim) {
87151508de1SMatthew Dillon 		return(-1);
87251508de1SMatthew Dillon 	}
87351508de1SMatthew Dillon 	return(0);
87451508de1SMatthew Dillon }
87551508de1SMatthew Dillon 
876