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" 380070e096SRobert Watson #include "opt_mac.h" 396a800098SYoshinobu Inoue 40df8bae1dSRodney W. Grimes #include <sys/param.h> 41df8bae1dSRodney W. Grimes #include <sys/systm.h> 420070e096SRobert Watson #include <sys/mac.h> 43df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 44df8bae1dSRodney W. Grimes #include <sys/protosw.h> 45df8bae1dSRodney W. Grimes #include <sys/socket.h> 46df8bae1dSRodney W. Grimes #include <sys/time.h> 47df8bae1dSRodney W. Grimes #include <sys/kernel.h> 48b5e8ce9fSBruce Evans #include <sys/sysctl.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <net/if.h> 519494d596SBrooks Davis #include <net/if_types.h> 52df8bae1dSRodney W. Grimes #include <net/route.h> 53df8bae1dSRodney W. Grimes 545e2d0696SGarrett Wollman #define _IP_VHL 55df8bae1dSRodney W. Grimes #include <netinet/in.h> 56df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 57df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 58df8bae1dSRodney W. Grimes #include <netinet/ip.h> 59df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h> 60b5e8ce9fSBruce Evans #include <netinet/ip_var.h> 61df8bae1dSRodney W. Grimes #include <netinet/icmp_var.h> 62df8bae1dSRodney W. Grimes 636a800098SYoshinobu Inoue #ifdef IPSEC 646a800098SYoshinobu Inoue #include <netinet6/ipsec.h> 656a800098SYoshinobu Inoue #include <netkey/key.h> 666a800098SYoshinobu Inoue #endif 676a800098SYoshinobu Inoue 6872a52a35SJonathan Lemon #include <machine/in_cksum.h> 6972a52a35SJonathan Lemon 70df8bae1dSRodney W. Grimes /* 71df8bae1dSRodney W. Grimes * ICMP routines: error generation, receive packet processing, and 72df8bae1dSRodney W. Grimes * routines to turnaround packets back to the originator, and 73df8bae1dSRodney W. Grimes * host table maintenance routines. 74df8bae1dSRodney W. Grimes */ 75df8bae1dSRodney W. Grimes 76f708ef1bSPoul-Henning Kamp static struct icmpstat icmpstat; 77c73d99b5SRuslan Ermilov SYSCTL_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RW, 780312fbe9SPoul-Henning Kamp &icmpstat, icmpstat, ""); 790312fbe9SPoul-Henning Kamp 800312fbe9SPoul-Henning Kamp static int icmpmaskrepl = 0; 810312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW, 820312fbe9SPoul-Henning Kamp &icmpmaskrepl, 0, ""); 830312fbe9SPoul-Henning Kamp 8418d3153eSDag-Erling Smørgrav static int drop_redirect = 0; 8518d3153eSDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_RW, 8618d3153eSDag-Erling Smørgrav &drop_redirect, 0, ""); 8718d3153eSDag-Erling Smørgrav 886c3b5f69SDag-Erling Smørgrav static int log_redirect = 0; 896c3b5f69SDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW, 906c3b5f69SDag-Erling Smørgrav &log_redirect, 0, ""); 916c3b5f69SDag-Erling Smørgrav 92173c0f9fSWarner Losh static int icmplim = 200; 9351508de1SMatthew Dillon SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW, 9451508de1SMatthew Dillon &icmplim, 0, ""); 955fce7fc4SMatthew Dillon 964f14ee00SDan Moschuk static int icmplim_output = 1; 974f14ee00SDan Moschuk SYSCTL_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_RW, 984f14ee00SDan Moschuk &icmplim_output, 0, ""); 9951508de1SMatthew Dillon 1005fce7fc4SMatthew Dillon /* 1015fce7fc4SMatthew Dillon * ICMP broadcast echo sysctl 1025fce7fc4SMatthew Dillon */ 1035fce7fc4SMatthew Dillon 10461a4defdSJoseph Koshy static int icmpbmcastecho = 0; 10518d3153eSDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_RW, 10618d3153eSDag-Erling Smørgrav &icmpbmcastecho, 0, ""); 1077022ea0aSGarrett Wollman 10851508de1SMatthew Dillon 109df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 110df8bae1dSRodney W. Grimes int icmpprintfs = 0; 111df8bae1dSRodney W. Grimes #endif 112df8bae1dSRodney W. Grimes 1134d77a549SAlfred Perlstein static void icmp_reflect(struct mbuf *); 1144d77a549SAlfred Perlstein static void icmp_send(struct mbuf *, struct mbuf *, struct route *); 1154d77a549SAlfred Perlstein static int ip_next_mtu(int, int); 1160312fbe9SPoul-Henning Kamp 117df8bae1dSRodney W. Grimes extern struct protosw inetsw[]; 118df8bae1dSRodney W. Grimes 119df8bae1dSRodney W. Grimes /* 120df8bae1dSRodney W. Grimes * Generate an error packet of type error 121df8bae1dSRodney W. Grimes * in response to bad packet ip. 122df8bae1dSRodney W. Grimes */ 123df8bae1dSRodney W. Grimes void 124df8bae1dSRodney W. Grimes icmp_error(n, type, code, dest, destifp) 125df8bae1dSRodney W. Grimes struct mbuf *n; 126df8bae1dSRodney W. Grimes int type, code; 127df8bae1dSRodney W. Grimes n_long dest; 128df8bae1dSRodney W. Grimes struct ifnet *destifp; 129df8bae1dSRodney W. Grimes { 130df8bae1dSRodney W. Grimes register struct ip *oip = mtod(n, struct ip *), *nip; 1315e2d0696SGarrett Wollman register unsigned oiplen = IP_VHL_HL(oip->ip_vhl) << 2; 132df8bae1dSRodney W. Grimes register struct icmp *icp; 133df8bae1dSRodney W. Grimes register struct mbuf *m; 134df8bae1dSRodney W. Grimes unsigned icmplen; 135df8bae1dSRodney W. Grimes 136df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 137df8bae1dSRodney W. Grimes if (icmpprintfs) 138623ae52eSPoul-Henning Kamp printf("icmp_error(%p, %x, %d)\n", oip, type, code); 139df8bae1dSRodney W. Grimes #endif 140df8bae1dSRodney W. Grimes if (type != ICMP_REDIRECT) 141df8bae1dSRodney W. Grimes icmpstat.icps_error++; 142df8bae1dSRodney W. Grimes /* 143df8bae1dSRodney W. Grimes * Don't send error if not the first fragment of message. 144df8bae1dSRodney W. Grimes * Don't error if the old packet protocol was ICMP 145df8bae1dSRodney W. Grimes * error message, only known informational types. 146df8bae1dSRodney W. Grimes */ 147df8bae1dSRodney W. Grimes if (oip->ip_off &~ (IP_MF|IP_DF)) 148df8bae1dSRodney W. Grimes goto freeit; 149df8bae1dSRodney W. Grimes if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT && 150df8bae1dSRodney W. Grimes n->m_len >= oiplen + ICMP_MINLEN && 151df8bae1dSRodney W. Grimes !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) { 152df8bae1dSRodney W. Grimes icmpstat.icps_oldicmp++; 153df8bae1dSRodney W. Grimes goto freeit; 154df8bae1dSRodney W. Grimes } 155df8bae1dSRodney W. Grimes /* Don't send error in response to a multicast or broadcast packet */ 156df8bae1dSRodney W. Grimes if (n->m_flags & (M_BCAST|M_MCAST)) 157df8bae1dSRodney W. Grimes goto freeit; 158df8bae1dSRodney W. Grimes /* 159df8bae1dSRodney W. Grimes * First, formulate icmp message 160df8bae1dSRodney W. Grimes */ 161df8bae1dSRodney W. Grimes m = m_gethdr(M_DONTWAIT, MT_HEADER); 162df8bae1dSRodney W. Grimes if (m == NULL) 163df8bae1dSRodney W. Grimes goto freeit; 1640070e096SRobert Watson #ifdef MAC 1650070e096SRobert Watson mac_create_mbuf_netlayer(n, m); 1660070e096SRobert Watson #endif 1671d027522SRuslan Ermilov icmplen = min(oiplen + 8, oip->ip_len); 168bfef7ed4SIan Dowse if (icmplen < sizeof(struct ip)) 169bfef7ed4SIan Dowse panic("icmp_error: bad length"); 170df8bae1dSRodney W. Grimes m->m_len = icmplen + ICMP_MINLEN; 171df8bae1dSRodney W. Grimes MH_ALIGN(m, m->m_len); 172df8bae1dSRodney W. Grimes icp = mtod(m, struct icmp *); 173df8bae1dSRodney W. Grimes if ((u_int)type > ICMP_MAXTYPE) 174df8bae1dSRodney W. Grimes panic("icmp_error"); 175df8bae1dSRodney W. Grimes icmpstat.icps_outhist[type]++; 176df8bae1dSRodney W. Grimes icp->icmp_type = type; 177df8bae1dSRodney W. Grimes if (type == ICMP_REDIRECT) 178df8bae1dSRodney W. Grimes icp->icmp_gwaddr.s_addr = dest; 179df8bae1dSRodney W. Grimes else { 180df8bae1dSRodney W. Grimes icp->icmp_void = 0; 181df8bae1dSRodney W. Grimes /* 182df8bae1dSRodney W. Grimes * The following assignments assume an overlay with the 183df8bae1dSRodney W. Grimes * zeroed icmp_void field. 184df8bae1dSRodney W. Grimes */ 185df8bae1dSRodney W. Grimes if (type == ICMP_PARAMPROB) { 186df8bae1dSRodney W. Grimes icp->icmp_pptr = code; 187df8bae1dSRodney W. Grimes code = 0; 188df8bae1dSRodney W. Grimes } else if (type == ICMP_UNREACH && 189df8bae1dSRodney W. Grimes code == ICMP_UNREACH_NEEDFRAG && destifp) { 190df8bae1dSRodney W. Grimes icp->icmp_nextmtu = htons(destifp->if_mtu); 191df8bae1dSRodney W. Grimes } 192df8bae1dSRodney W. Grimes } 193df8bae1dSRodney W. Grimes 194df8bae1dSRodney W. Grimes icp->icmp_code = code; 195bfef7ed4SIan Dowse m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip); 196df8bae1dSRodney W. Grimes nip = &icp->icmp_ip; 19704287599SRuslan Ermilov 19804287599SRuslan Ermilov /* 19904287599SRuslan Ermilov * Convert fields to network representation. 20004287599SRuslan Ermilov */ 201fd8e4ebcSMike Barcroft nip->ip_len = htons(nip->ip_len); 202fd8e4ebcSMike Barcroft nip->ip_off = htons(nip->ip_off); 203df8bae1dSRodney W. Grimes 204df8bae1dSRodney W. Grimes /* 205df8bae1dSRodney W. Grimes * Now, copy old ip header (without options) 206df8bae1dSRodney W. Grimes * in front of icmp message. 207df8bae1dSRodney W. Grimes */ 208df8bae1dSRodney W. Grimes if (m->m_data - sizeof(struct ip) < m->m_pktdat) 209df8bae1dSRodney W. Grimes panic("icmp len"); 210df8bae1dSRodney W. Grimes m->m_data -= sizeof(struct ip); 211df8bae1dSRodney W. Grimes m->m_len += sizeof(struct ip); 212df8bae1dSRodney W. Grimes m->m_pkthdr.len = m->m_len; 213df8bae1dSRodney W. Grimes m->m_pkthdr.rcvif = n->m_pkthdr.rcvif; 214df8bae1dSRodney W. Grimes nip = mtod(m, struct ip *); 215df8bae1dSRodney W. Grimes bcopy((caddr_t)oip, (caddr_t)nip, sizeof(struct ip)); 216df8bae1dSRodney W. Grimes nip->ip_len = m->m_len; 2175e2d0696SGarrett Wollman nip->ip_vhl = IP_VHL_BORING; 218df8bae1dSRodney W. Grimes nip->ip_p = IPPROTO_ICMP; 219df8bae1dSRodney W. Grimes nip->ip_tos = 0; 220df8bae1dSRodney W. Grimes icmp_reflect(m); 221df8bae1dSRodney W. Grimes 222df8bae1dSRodney W. Grimes freeit: 223df8bae1dSRodney W. Grimes m_freem(n); 224df8bae1dSRodney W. Grimes } 225df8bae1dSRodney W. Grimes 226df8bae1dSRodney W. Grimes static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET }; 227df8bae1dSRodney W. Grimes static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET }; 228df8bae1dSRodney W. Grimes static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET }; 229df8bae1dSRodney W. Grimes 230df8bae1dSRodney W. Grimes /* 231df8bae1dSRodney W. Grimes * Process a received ICMP message. 232df8bae1dSRodney W. Grimes */ 233df8bae1dSRodney W. Grimes void 234f0ffb944SJulian Elischer icmp_input(m, off) 235df8bae1dSRodney W. Grimes register struct mbuf *m; 236f0ffb944SJulian Elischer int off; 237df8bae1dSRodney W. Grimes { 2386a800098SYoshinobu Inoue int hlen = off; 239df8bae1dSRodney W. Grimes register struct icmp *icp; 240df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 241df8bae1dSRodney W. Grimes int icmplen = ip->ip_len; 242df8bae1dSRodney W. Grimes register int i; 243df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 2444d77a549SAlfred Perlstein void (*ctlfunc)(int, struct sockaddr *, void *); 245df8bae1dSRodney W. Grimes int code; 246df8bae1dSRodney W. Grimes 247df8bae1dSRodney W. Grimes /* 248df8bae1dSRodney W. Grimes * Locate icmp structure in mbuf, and check 249df8bae1dSRodney W. Grimes * that not corrupted and of at least minimum length. 250df8bae1dSRodney W. Grimes */ 251df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 2522b758395SGarrett Wollman if (icmpprintfs) { 2532b758395SGarrett Wollman char buf[4 * sizeof "123"]; 2542b758395SGarrett Wollman strcpy(buf, inet_ntoa(ip->ip_src)); 2552b758395SGarrett Wollman printf("icmp_input from %s to %s, len %d\n", 2562b758395SGarrett Wollman buf, inet_ntoa(ip->ip_dst), icmplen); 2572b758395SGarrett Wollman } 258df8bae1dSRodney W. Grimes #endif 259df8bae1dSRodney W. Grimes if (icmplen < ICMP_MINLEN) { 260df8bae1dSRodney W. Grimes icmpstat.icps_tooshort++; 261df8bae1dSRodney W. Grimes goto freeit; 262df8bae1dSRodney W. Grimes } 263df8bae1dSRodney W. Grimes i = hlen + min(icmplen, ICMP_ADVLENMIN); 264df8bae1dSRodney W. Grimes if (m->m_len < i && (m = m_pullup(m, i)) == 0) { 265df8bae1dSRodney W. Grimes icmpstat.icps_tooshort++; 266df8bae1dSRodney W. Grimes return; 267df8bae1dSRodney W. Grimes } 268df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 269df8bae1dSRodney W. Grimes m->m_len -= hlen; 270df8bae1dSRodney W. Grimes m->m_data += hlen; 271df8bae1dSRodney W. Grimes icp = mtod(m, struct icmp *); 272df8bae1dSRodney W. Grimes if (in_cksum(m, icmplen)) { 273df8bae1dSRodney W. Grimes icmpstat.icps_checksum++; 274df8bae1dSRodney W. Grimes goto freeit; 275df8bae1dSRodney W. Grimes } 276df8bae1dSRodney W. Grimes m->m_len += hlen; 277df8bae1dSRodney W. Grimes m->m_data -= hlen; 278df8bae1dSRodney W. Grimes 2796a800098SYoshinobu Inoue if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { 2806a800098SYoshinobu Inoue /* 2816a800098SYoshinobu Inoue * Deliver very specific ICMP type only. 2826a800098SYoshinobu Inoue */ 2836a800098SYoshinobu Inoue switch (icp->icmp_type) { 2846a800098SYoshinobu Inoue case ICMP_UNREACH: 2856a800098SYoshinobu Inoue case ICMP_TIMXCEED: 2866a800098SYoshinobu Inoue break; 2876a800098SYoshinobu Inoue default: 2886a800098SYoshinobu Inoue goto freeit; 2896a800098SYoshinobu Inoue } 2906a800098SYoshinobu Inoue } 2916a800098SYoshinobu Inoue 292df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 293df8bae1dSRodney W. Grimes if (icmpprintfs) 294df8bae1dSRodney W. Grimes printf("icmp_input, type %d code %d\n", icp->icmp_type, 295df8bae1dSRodney W. Grimes icp->icmp_code); 296df8bae1dSRodney W. Grimes #endif 2975b7ee6edSGarrett Wollman 2985b7ee6edSGarrett Wollman /* 2995b7ee6edSGarrett Wollman * Message type specific processing. 3005b7ee6edSGarrett Wollman */ 301df8bae1dSRodney W. Grimes if (icp->icmp_type > ICMP_MAXTYPE) 302df8bae1dSRodney W. Grimes goto raw; 303df8bae1dSRodney W. Grimes icmpstat.icps_inhist[icp->icmp_type]++; 304df8bae1dSRodney W. Grimes code = icp->icmp_code; 305df8bae1dSRodney W. Grimes switch (icp->icmp_type) { 306df8bae1dSRodney W. Grimes 307df8bae1dSRodney W. Grimes case ICMP_UNREACH: 308df8bae1dSRodney W. Grimes switch (code) { 309df8bae1dSRodney W. Grimes case ICMP_UNREACH_NET: 310df8bae1dSRodney W. Grimes case ICMP_UNREACH_HOST: 311df8bae1dSRodney W. Grimes case ICMP_UNREACH_SRCFAIL: 312e4bb5b05SJonathan Lemon case ICMP_UNREACH_NET_UNKNOWN: 313e4bb5b05SJonathan Lemon case ICMP_UNREACH_HOST_UNKNOWN: 314e4bb5b05SJonathan Lemon case ICMP_UNREACH_ISOLATED: 315e4bb5b05SJonathan Lemon case ICMP_UNREACH_TOSNET: 316e4bb5b05SJonathan Lemon case ICMP_UNREACH_TOSHOST: 317e4bb5b05SJonathan Lemon case ICMP_UNREACH_HOST_PRECEDENCE: 318e4bb5b05SJonathan Lemon case ICMP_UNREACH_PRECEDENCE_CUTOFF: 319e4bb5b05SJonathan Lemon code = PRC_UNREACH_NET; 320df8bae1dSRodney W. Grimes break; 321df8bae1dSRodney W. Grimes 322df8bae1dSRodney W. Grimes case ICMP_UNREACH_NEEDFRAG: 323df8bae1dSRodney W. Grimes code = PRC_MSGSIZE; 324df8bae1dSRodney W. Grimes break; 325df8bae1dSRodney W. Grimes 326e4bb5b05SJonathan Lemon /* 327e4bb5b05SJonathan Lemon * RFC 1122, Sections 3.2.2.1 and 4.2.3.9. 328e4bb5b05SJonathan Lemon * Treat subcodes 2,3 as immediate RST 329e4bb5b05SJonathan Lemon */ 330e4bb5b05SJonathan Lemon case ICMP_UNREACH_PROTOCOL: 331e4bb5b05SJonathan Lemon case ICMP_UNREACH_PORT: 332b77d155dSJesper Skriver code = PRC_UNREACH_PORT; 333b11d7a4aSPoul-Henning Kamp break; 33490fcbbd6SPoul-Henning Kamp 33590fcbbd6SPoul-Henning Kamp case ICMP_UNREACH_NET_PROHIB: 33690fcbbd6SPoul-Henning Kamp case ICMP_UNREACH_HOST_PROHIB: 3379c4b2574SPaul Traina case ICMP_UNREACH_FILTER_PROHIB: 33890fcbbd6SPoul-Henning Kamp code = PRC_UNREACH_ADMIN_PROHIB; 339b11d7a4aSPoul-Henning Kamp break; 340b11d7a4aSPoul-Henning Kamp 341df8bae1dSRodney W. Grimes default: 342df8bae1dSRodney W. Grimes goto badcode; 343df8bae1dSRodney W. Grimes } 344df8bae1dSRodney W. Grimes goto deliver; 345df8bae1dSRodney W. Grimes 346df8bae1dSRodney W. Grimes case ICMP_TIMXCEED: 347df8bae1dSRodney W. Grimes if (code > 1) 348df8bae1dSRodney W. Grimes goto badcode; 349df8bae1dSRodney W. Grimes code += PRC_TIMXCEED_INTRANS; 350df8bae1dSRodney W. Grimes goto deliver; 351df8bae1dSRodney W. Grimes 352df8bae1dSRodney W. Grimes case ICMP_PARAMPROB: 353df8bae1dSRodney W. Grimes if (code > 1) 354df8bae1dSRodney W. Grimes goto badcode; 355df8bae1dSRodney W. Grimes code = PRC_PARAMPROB; 356df8bae1dSRodney W. Grimes goto deliver; 357df8bae1dSRodney W. Grimes 358df8bae1dSRodney W. Grimes case ICMP_SOURCEQUENCH: 359df8bae1dSRodney W. Grimes if (code) 360df8bae1dSRodney W. Grimes goto badcode; 361df8bae1dSRodney W. Grimes code = PRC_QUENCH; 362df8bae1dSRodney W. Grimes deliver: 363df8bae1dSRodney W. Grimes /* 364df8bae1dSRodney W. Grimes * Problem with datagram; advise higher level routines. 365df8bae1dSRodney W. Grimes */ 366df8bae1dSRodney W. Grimes if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) || 3675e2d0696SGarrett Wollman IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) { 368df8bae1dSRodney W. Grimes icmpstat.icps_badlen++; 369df8bae1dSRodney W. Grimes goto freeit; 370df8bae1dSRodney W. Grimes } 371fd8e4ebcSMike Barcroft icp->icmp_ip.ip_len = ntohs(icp->icmp_ip.ip_len); 3725b7ee6edSGarrett Wollman /* Discard ICMP's in response to multicast packets */ 3735b7ee6edSGarrett Wollman if (IN_MULTICAST(ntohl(icp->icmp_ip.ip_dst.s_addr))) 3745b7ee6edSGarrett Wollman goto badcode; 375df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 376df8bae1dSRodney W. Grimes if (icmpprintfs) 377df8bae1dSRodney W. Grimes printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); 378df8bae1dSRodney W. Grimes #endif 379df8bae1dSRodney W. Grimes icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 380b7a44e34SGarrett Wollman #if 1 3815cbf3e08SGarrett Wollman /* 3825cbf3e08SGarrett Wollman * MTU discovery: 3835cbf3e08SGarrett Wollman * If we got a needfrag and there is a host route to the 3845cbf3e08SGarrett Wollman * original destination, and the MTU is not locked, then 3855cbf3e08SGarrett Wollman * set the MTU in the route to the suggested new value 3865cbf3e08SGarrett Wollman * (if given) and then notify as usual. The ULPs will 3875cbf3e08SGarrett Wollman * notice that the MTU has changed and adapt accordingly. 3885cbf3e08SGarrett Wollman * If no new MTU was suggested, then we guess a new one 3895cbf3e08SGarrett Wollman * less than the current value. If the new MTU is 3905cbf3e08SGarrett Wollman * unreasonably small (arbitrarily set at 296), then 3915cbf3e08SGarrett Wollman * we reset the MTU to the interface value and enable the 3925cbf3e08SGarrett Wollman * lock bit, indicating that we are no longer doing MTU 3935cbf3e08SGarrett Wollman * discovery. 3945cbf3e08SGarrett Wollman */ 3955cbf3e08SGarrett Wollman if (code == PRC_MSGSIZE) { 3965cbf3e08SGarrett Wollman struct rtentry *rt; 3975cbf3e08SGarrett Wollman int mtu; 3985cbf3e08SGarrett Wollman 3995cbf3e08SGarrett Wollman rt = rtalloc1((struct sockaddr *)&icmpsrc, 0, 4005cbf3e08SGarrett Wollman RTF_CLONING | RTF_PRCLONING); 4015cbf3e08SGarrett Wollman if (rt && (rt->rt_flags & RTF_HOST) 4025cbf3e08SGarrett Wollman && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { 4035cbf3e08SGarrett Wollman mtu = ntohs(icp->icmp_nextmtu); 4045cbf3e08SGarrett Wollman if (!mtu) 4055cbf3e08SGarrett Wollman mtu = ip_next_mtu(rt->rt_rmx.rmx_mtu, 4065cbf3e08SGarrett Wollman 1); 407be070f43SGarrett Wollman #ifdef DEBUG_MTUDISC 408be070f43SGarrett Wollman printf("MTU for %s reduced to %d\n", 409be070f43SGarrett Wollman inet_ntoa(icmpsrc.sin_addr), mtu); 410be070f43SGarrett Wollman #endif 411be070f43SGarrett Wollman if (mtu < 296) { 412b7a44e34SGarrett Wollman /* rt->rt_rmx.rmx_mtu = 413b7a44e34SGarrett Wollman rt->rt_ifp->if_mtu; */ 4145cbf3e08SGarrett Wollman rt->rt_rmx.rmx_locks |= RTV_MTU; 4155cbf3e08SGarrett Wollman } else if (rt->rt_rmx.rmx_mtu > mtu) { 4165cbf3e08SGarrett Wollman rt->rt_rmx.rmx_mtu = mtu; 4175cbf3e08SGarrett Wollman } 4185cbf3e08SGarrett Wollman } 4195cbf3e08SGarrett Wollman if (rt) 4205cbf3e08SGarrett Wollman RTFREE(rt); 4215cbf3e08SGarrett Wollman } 4225cbf3e08SGarrett Wollman 423b7a44e34SGarrett Wollman #endif 4246a800098SYoshinobu Inoue /* 4256a800098SYoshinobu Inoue * XXX if the packet contains [IPv4 AH TCP], we can't make a 4266a800098SYoshinobu Inoue * notification to TCP layer. 4276a800098SYoshinobu Inoue */ 428623ae52eSPoul-Henning Kamp ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput; 429623ae52eSPoul-Henning Kamp if (ctlfunc) 430df8bae1dSRodney W. Grimes (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, 431b62d102cSBruce Evans (void *)&icp->icmp_ip); 432df8bae1dSRodney W. Grimes break; 433df8bae1dSRodney W. Grimes 434df8bae1dSRodney W. Grimes badcode: 435df8bae1dSRodney W. Grimes icmpstat.icps_badcode++; 436df8bae1dSRodney W. Grimes break; 437df8bae1dSRodney W. Grimes 438df8bae1dSRodney W. Grimes case ICMP_ECHO: 4397022ea0aSGarrett Wollman if (!icmpbmcastecho 440d311884fSDavid Greenman && (m->m_flags & (M_MCAST | M_BCAST)) != 0) { 4417022ea0aSGarrett Wollman icmpstat.icps_bmcastecho++; 4427022ea0aSGarrett Wollman break; 4437022ea0aSGarrett Wollman } 444df8bae1dSRodney W. Grimes icp->icmp_type = ICMP_ECHOREPLY; 445a57815efSBosko Milekic if (badport_bandlim(BANDLIM_ICMP_ECHO) < 0) 44609f81a46SBosko Milekic goto freeit; 44709f81a46SBosko Milekic else 448df8bae1dSRodney W. Grimes goto reflect; 449df8bae1dSRodney W. Grimes 450df8bae1dSRodney W. Grimes case ICMP_TSTAMP: 451fe0fb8abSGarrett Wollman if (!icmpbmcastecho 452d311884fSDavid Greenman && (m->m_flags & (M_MCAST | M_BCAST)) != 0) { 453fe0fb8abSGarrett Wollman icmpstat.icps_bmcasttstamp++; 454fe0fb8abSGarrett Wollman break; 455fe0fb8abSGarrett Wollman } 456df8bae1dSRodney W. Grimes if (icmplen < ICMP_TSLEN) { 457df8bae1dSRodney W. Grimes icmpstat.icps_badlen++; 458df8bae1dSRodney W. Grimes break; 459df8bae1dSRodney W. Grimes } 460df8bae1dSRodney W. Grimes icp->icmp_type = ICMP_TSTAMPREPLY; 461df8bae1dSRodney W. Grimes icp->icmp_rtime = iptime(); 462df8bae1dSRodney W. Grimes icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */ 463a57815efSBosko Milekic if (badport_bandlim(BANDLIM_ICMP_TSTAMP) < 0) 46409f81a46SBosko Milekic goto freeit; 46509f81a46SBosko Milekic else 466df8bae1dSRodney W. Grimes goto reflect; 467df8bae1dSRodney W. Grimes 468df8bae1dSRodney W. Grimes case ICMP_MASKREQ: 469df8bae1dSRodney W. Grimes if (icmpmaskrepl == 0) 470df8bae1dSRodney W. Grimes break; 471df8bae1dSRodney W. Grimes /* 472df8bae1dSRodney W. Grimes * We are not able to respond with all ones broadcast 473df8bae1dSRodney W. Grimes * unless we receive it over a point-to-point interface. 474df8bae1dSRodney W. Grimes */ 475df8bae1dSRodney W. Grimes if (icmplen < ICMP_MASKLEN) 476df8bae1dSRodney W. Grimes break; 477df8bae1dSRodney W. Grimes switch (ip->ip_dst.s_addr) { 478df8bae1dSRodney W. Grimes 479df8bae1dSRodney W. Grimes case INADDR_BROADCAST: 480df8bae1dSRodney W. Grimes case INADDR_ANY: 481df8bae1dSRodney W. Grimes icmpdst.sin_addr = ip->ip_src; 482df8bae1dSRodney W. Grimes break; 483df8bae1dSRodney W. Grimes 484df8bae1dSRodney W. Grimes default: 485df8bae1dSRodney W. Grimes icmpdst.sin_addr = ip->ip_dst; 486df8bae1dSRodney W. Grimes } 487df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)ifaof_ifpforaddr( 488df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif); 489df8bae1dSRodney W. Grimes if (ia == 0) 490df8bae1dSRodney W. Grimes break; 4917e6f7714SPoul-Henning Kamp if (ia->ia_ifp == 0) 4927e6f7714SPoul-Henning Kamp break; 493df8bae1dSRodney W. Grimes icp->icmp_type = ICMP_MASKREPLY; 494df8bae1dSRodney W. Grimes icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr; 495df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == 0) { 496df8bae1dSRodney W. Grimes if (ia->ia_ifp->if_flags & IFF_BROADCAST) 497df8bae1dSRodney W. Grimes ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; 498df8bae1dSRodney W. Grimes else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) 499df8bae1dSRodney W. Grimes ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; 500df8bae1dSRodney W. Grimes } 501df8bae1dSRodney W. Grimes reflect: 502df8bae1dSRodney W. Grimes ip->ip_len += hlen; /* since ip_input deducts this */ 503df8bae1dSRodney W. Grimes icmpstat.icps_reflect++; 504df8bae1dSRodney W. Grimes icmpstat.icps_outhist[icp->icmp_type]++; 505df8bae1dSRodney W. Grimes icmp_reflect(m); 506df8bae1dSRodney W. Grimes return; 507df8bae1dSRodney W. Grimes 508df8bae1dSRodney W. Grimes case ICMP_REDIRECT: 50918d3153eSDag-Erling Smørgrav if (log_redirect) { 51018d3153eSDag-Erling Smørgrav u_long src, dst, gw; 51118d3153eSDag-Erling Smørgrav 51218d3153eSDag-Erling Smørgrav src = ntohl(ip->ip_src.s_addr); 51318d3153eSDag-Erling Smørgrav dst = ntohl(icp->icmp_ip.ip_dst.s_addr); 51418d3153eSDag-Erling Smørgrav gw = ntohl(icp->icmp_gwaddr.s_addr); 51518d3153eSDag-Erling Smørgrav printf("icmp redirect from %d.%d.%d.%d: " 51618d3153eSDag-Erling Smørgrav "%d.%d.%d.%d => %d.%d.%d.%d\n", 51718d3153eSDag-Erling Smørgrav (int)(src >> 24), (int)((src >> 16) & 0xff), 51818d3153eSDag-Erling Smørgrav (int)((src >> 8) & 0xff), (int)(src & 0xff), 51918d3153eSDag-Erling Smørgrav (int)(dst >> 24), (int)((dst >> 16) & 0xff), 52018d3153eSDag-Erling Smørgrav (int)((dst >> 8) & 0xff), (int)(dst & 0xff), 52118d3153eSDag-Erling Smørgrav (int)(gw >> 24), (int)((gw >> 16) & 0xff), 52218d3153eSDag-Erling Smørgrav (int)((gw >> 8) & 0xff), (int)(gw & 0xff)); 52318d3153eSDag-Erling Smørgrav } 52418d3153eSDag-Erling Smørgrav if (drop_redirect) 52518d3153eSDag-Erling Smørgrav break; 526df8bae1dSRodney W. Grimes if (code > 3) 527df8bae1dSRodney W. Grimes goto badcode; 528df8bae1dSRodney W. Grimes if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) || 5295e2d0696SGarrett Wollman IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) { 530df8bae1dSRodney W. Grimes icmpstat.icps_badlen++; 531df8bae1dSRodney W. Grimes break; 532df8bae1dSRodney W. Grimes } 533df8bae1dSRodney W. Grimes /* 534df8bae1dSRodney W. Grimes * Short circuit routing redirects to force 535df8bae1dSRodney W. Grimes * immediate change in the kernel's routing 536df8bae1dSRodney W. Grimes * tables. The message is also handed to anyone 537df8bae1dSRodney W. Grimes * listening on a raw socket (e.g. the routing 538df8bae1dSRodney W. Grimes * daemon for use in updating its tables). 539df8bae1dSRodney W. Grimes */ 540df8bae1dSRodney W. Grimes icmpgw.sin_addr = ip->ip_src; 541df8bae1dSRodney W. Grimes icmpdst.sin_addr = icp->icmp_gwaddr; 542df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 5432b758395SGarrett Wollman if (icmpprintfs) { 5442b758395SGarrett Wollman char buf[4 * sizeof "123"]; 5452b758395SGarrett Wollman strcpy(buf, inet_ntoa(icp->icmp_ip.ip_dst)); 5462b758395SGarrett Wollman 5472b758395SGarrett Wollman printf("redirect dst %s to %s\n", 5482b758395SGarrett Wollman buf, inet_ntoa(icp->icmp_gwaddr)); 5492b758395SGarrett Wollman } 550df8bae1dSRodney W. Grimes #endif 551df8bae1dSRodney W. Grimes icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 552df8bae1dSRodney W. Grimes rtredirect((struct sockaddr *)&icmpsrc, 553df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpdst, 554df8bae1dSRodney W. Grimes (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST, 555df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpgw, (struct rtentry **)0); 556df8bae1dSRodney W. Grimes pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc); 5576a800098SYoshinobu Inoue #ifdef IPSEC 5586a800098SYoshinobu Inoue key_sa_routechange((struct sockaddr *)&icmpsrc); 5596a800098SYoshinobu Inoue #endif 560df8bae1dSRodney W. Grimes break; 561df8bae1dSRodney W. Grimes 562df8bae1dSRodney W. Grimes /* 563df8bae1dSRodney W. Grimes * No kernel processing for the following; 564df8bae1dSRodney W. Grimes * just fall through to send to raw listener. 565df8bae1dSRodney W. Grimes */ 566df8bae1dSRodney W. Grimes case ICMP_ECHOREPLY: 567df8bae1dSRodney W. Grimes case ICMP_ROUTERADVERT: 568df8bae1dSRodney W. Grimes case ICMP_ROUTERSOLICIT: 569df8bae1dSRodney W. Grimes case ICMP_TSTAMPREPLY: 570df8bae1dSRodney W. Grimes case ICMP_IREQREPLY: 571df8bae1dSRodney W. Grimes case ICMP_MASKREPLY: 572df8bae1dSRodney W. Grimes default: 573df8bae1dSRodney W. Grimes break; 574df8bae1dSRodney W. Grimes } 575df8bae1dSRodney W. Grimes 576df8bae1dSRodney W. Grimes raw: 577f0ffb944SJulian Elischer rip_input(m, off); 578df8bae1dSRodney W. Grimes return; 579df8bae1dSRodney W. Grimes 580df8bae1dSRodney W. Grimes freeit: 581df8bae1dSRodney W. Grimes m_freem(m); 582df8bae1dSRodney W. Grimes } 583df8bae1dSRodney W. Grimes 584df8bae1dSRodney W. Grimes /* 585df8bae1dSRodney W. Grimes * Reflect the ip packet back to the source 586df8bae1dSRodney W. Grimes */ 5870312fbe9SPoul-Henning Kamp static void 588df8bae1dSRodney W. Grimes icmp_reflect(m) 589df8bae1dSRodney W. Grimes struct mbuf *m; 590df8bae1dSRodney W. Grimes { 591ca925d9cSJonathan Lemon struct ip *ip = mtod(m, struct ip *); 592ca925d9cSJonathan Lemon struct ifaddr *ifa; 593ca925d9cSJonathan Lemon struct in_ifaddr *ia; 594df8bae1dSRodney W. Grimes struct in_addr t; 595b5e8ce9fSBruce Evans struct mbuf *opts = 0; 5965e2d0696SGarrett Wollman int optlen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof(struct ip); 597bd714208SRuslan Ermilov struct route *ro = NULL, rt; 598df8bae1dSRodney W. Grimes 599df8bae1dSRodney W. Grimes if (!in_canforward(ip->ip_src) && 600df8bae1dSRodney W. Grimes ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) != 601df8bae1dSRodney W. Grimes (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) { 602df8bae1dSRodney W. Grimes m_freem(m); /* Bad return address */ 603bd714208SRuslan Ermilov icmpstat.icps_badaddr++; 604df8bae1dSRodney W. Grimes goto done; /* Ip_output() will check for broadcast */ 605df8bae1dSRodney W. Grimes } 606df8bae1dSRodney W. Grimes t = ip->ip_dst; 607df8bae1dSRodney W. Grimes ip->ip_dst = ip->ip_src; 608e3f406b3SRuslan Ermilov ro = &rt; 609e3f406b3SRuslan Ermilov bzero(ro, sizeof(*ro)); 610df8bae1dSRodney W. Grimes /* 611df8bae1dSRodney W. Grimes * If the incoming packet was addressed directly to us, 612df8bae1dSRodney W. Grimes * use dst as the src for the reply. Otherwise (broadcast 613df8bae1dSRodney W. Grimes * or anonymous), use the address which corresponds 614df8bae1dSRodney W. Grimes * to the incoming interface. 615df8bae1dSRodney W. Grimes */ 616ca925d9cSJonathan Lemon LIST_FOREACH(ia, INADDR_HASH(t.s_addr), ia_hash) 617df8bae1dSRodney W. Grimes if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) 618ca925d9cSJonathan Lemon goto match; 61984caf008SRuslan Ermilov if (m->m_pkthdr.rcvif != NULL && 62084caf008SRuslan Ermilov m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) { 621ca925d9cSJonathan Lemon TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) { 622ca925d9cSJonathan Lemon if (ifa->ifa_addr->sa_family != AF_INET) 623ca925d9cSJonathan Lemon continue; 624ca925d9cSJonathan Lemon ia = ifatoia(ifa); 625ca925d9cSJonathan Lemon if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == 626ca925d9cSJonathan Lemon t.s_addr) 627ca925d9cSJonathan Lemon goto match; 628df8bae1dSRodney W. Grimes } 629ca925d9cSJonathan Lemon } 630bd714208SRuslan Ermilov ia = ip_rtaddr(ip->ip_dst, ro); 631bd714208SRuslan Ermilov /* We need a route to do anything useful. */ 6320d4bef5dSDima Dorfman if (ia == NULL) { 6330d4bef5dSDima Dorfman m_freem(m); 634bd714208SRuslan Ermilov icmpstat.icps_noroute++; 6350d4bef5dSDima Dorfman goto done; 6360d4bef5dSDima Dorfman } 637ca925d9cSJonathan Lemon match: 638df8bae1dSRodney W. Grimes t = IA_SIN(ia)->sin_addr; 639df8bae1dSRodney W. Grimes ip->ip_src = t; 6408ce3f3ddSRuslan Ermilov ip->ip_ttl = ip_defttl; 641df8bae1dSRodney W. Grimes 642df8bae1dSRodney W. Grimes if (optlen > 0) { 643df8bae1dSRodney W. Grimes register u_char *cp; 644df8bae1dSRodney W. Grimes int opt, cnt; 645df8bae1dSRodney W. Grimes u_int len; 646df8bae1dSRodney W. Grimes 647df8bae1dSRodney W. Grimes /* 648df8bae1dSRodney W. Grimes * Retrieve any source routing from the incoming packet; 649df8bae1dSRodney W. Grimes * add on any record-route or timestamp options. 650df8bae1dSRodney W. Grimes */ 651df8bae1dSRodney W. Grimes cp = (u_char *) (ip + 1); 652df8bae1dSRodney W. Grimes if ((opts = ip_srcroute()) == 0 && 653df8bae1dSRodney W. Grimes (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) { 654df8bae1dSRodney W. Grimes opts->m_len = sizeof(struct in_addr); 655df8bae1dSRodney W. Grimes mtod(opts, struct in_addr *)->s_addr = 0; 656df8bae1dSRodney W. Grimes } 657df8bae1dSRodney W. Grimes if (opts) { 658df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 659df8bae1dSRodney W. Grimes if (icmpprintfs) 660df8bae1dSRodney W. Grimes printf("icmp_reflect optlen %d rt %d => ", 661df8bae1dSRodney W. Grimes optlen, opts->m_len); 662df8bae1dSRodney W. Grimes #endif 663df8bae1dSRodney W. Grimes for (cnt = optlen; cnt > 0; cnt -= len, cp += len) { 664df8bae1dSRodney W. Grimes opt = cp[IPOPT_OPTVAL]; 665df8bae1dSRodney W. Grimes if (opt == IPOPT_EOL) 666df8bae1dSRodney W. Grimes break; 667df8bae1dSRodney W. Grimes if (opt == IPOPT_NOP) 668df8bae1dSRodney W. Grimes len = 1; 669df8bae1dSRodney W. Grimes else { 670707d00a3SJonathan Lemon if (cnt < IPOPT_OLEN + sizeof(*cp)) 671707d00a3SJonathan Lemon break; 672df8bae1dSRodney W. Grimes len = cp[IPOPT_OLEN]; 673707d00a3SJonathan Lemon if (len < IPOPT_OLEN + sizeof(*cp) || 674707d00a3SJonathan Lemon len > cnt) 675df8bae1dSRodney W. Grimes break; 676df8bae1dSRodney W. Grimes } 677df8bae1dSRodney W. Grimes /* 678df8bae1dSRodney W. Grimes * Should check for overflow, but it "can't happen" 679df8bae1dSRodney W. Grimes */ 680df8bae1dSRodney W. Grimes if (opt == IPOPT_RR || opt == IPOPT_TS || 681df8bae1dSRodney W. Grimes opt == IPOPT_SECURITY) { 682df8bae1dSRodney W. Grimes bcopy((caddr_t)cp, 683df8bae1dSRodney W. Grimes mtod(opts, caddr_t) + opts->m_len, len); 684df8bae1dSRodney W. Grimes opts->m_len += len; 685df8bae1dSRodney W. Grimes } 686df8bae1dSRodney W. Grimes } 687df8bae1dSRodney W. Grimes /* Terminate & pad, if necessary */ 688623ae52eSPoul-Henning Kamp cnt = opts->m_len % 4; 689623ae52eSPoul-Henning Kamp if (cnt) { 690df8bae1dSRodney W. Grimes for (; cnt < 4; cnt++) { 691df8bae1dSRodney W. Grimes *(mtod(opts, caddr_t) + opts->m_len) = 692df8bae1dSRodney W. Grimes IPOPT_EOL; 693df8bae1dSRodney W. Grimes opts->m_len++; 694df8bae1dSRodney W. Grimes } 695df8bae1dSRodney W. Grimes } 696df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 697df8bae1dSRodney W. Grimes if (icmpprintfs) 698df8bae1dSRodney W. Grimes printf("%d\n", opts->m_len); 699df8bae1dSRodney W. Grimes #endif 700df8bae1dSRodney W. Grimes } 701df8bae1dSRodney W. Grimes /* 702df8bae1dSRodney W. Grimes * Now strip out original options by copying rest of first 703df8bae1dSRodney W. Grimes * mbuf's data back, and adjust the IP length. 704df8bae1dSRodney W. Grimes */ 705df8bae1dSRodney W. Grimes ip->ip_len -= optlen; 7065e2d0696SGarrett Wollman ip->ip_vhl = IP_VHL_BORING; 707df8bae1dSRodney W. Grimes m->m_len -= optlen; 708df8bae1dSRodney W. Grimes if (m->m_flags & M_PKTHDR) 709df8bae1dSRodney W. Grimes m->m_pkthdr.len -= optlen; 710df8bae1dSRodney W. Grimes optlen += sizeof(struct ip); 711df8bae1dSRodney W. Grimes bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1), 712df8bae1dSRodney W. Grimes (unsigned)(m->m_len - sizeof(struct ip))); 713df8bae1dSRodney W. Grimes } 714df8bae1dSRodney W. Grimes m->m_flags &= ~(M_BCAST|M_MCAST); 715bd714208SRuslan Ermilov icmp_send(m, opts, ro); 716df8bae1dSRodney W. Grimes done: 717df8bae1dSRodney W. Grimes if (opts) 718df8bae1dSRodney W. Grimes (void)m_free(opts); 719bd714208SRuslan Ermilov if (ro && ro->ro_rt) 720bd714208SRuslan Ermilov RTFREE(ro->ro_rt); 721df8bae1dSRodney W. Grimes } 722df8bae1dSRodney W. Grimes 723df8bae1dSRodney W. Grimes /* 724df8bae1dSRodney W. Grimes * Send an icmp packet back to the ip level, 725df8bae1dSRodney W. Grimes * after supplying a checksum. 726df8bae1dSRodney W. Grimes */ 7270312fbe9SPoul-Henning Kamp static void 728bd714208SRuslan Ermilov icmp_send(m, opts, rt) 729df8bae1dSRodney W. Grimes register struct mbuf *m; 730df8bae1dSRodney W. Grimes struct mbuf *opts; 731bd714208SRuslan Ermilov struct route *rt; 732df8bae1dSRodney W. Grimes { 733df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 734df8bae1dSRodney W. Grimes register int hlen; 735df8bae1dSRodney W. Grimes register struct icmp *icp; 736df8bae1dSRodney W. Grimes 7375e2d0696SGarrett Wollman hlen = IP_VHL_HL(ip->ip_vhl) << 2; 738df8bae1dSRodney W. Grimes m->m_data += hlen; 739df8bae1dSRodney W. Grimes m->m_len -= hlen; 740df8bae1dSRodney W. Grimes icp = mtod(m, struct icmp *); 741df8bae1dSRodney W. Grimes icp->icmp_cksum = 0; 742df8bae1dSRodney W. Grimes icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); 743df8bae1dSRodney W. Grimes m->m_data -= hlen; 744df8bae1dSRodney W. Grimes m->m_len += hlen; 74594446a2eSArchie Cobbs m->m_pkthdr.rcvif = (struct ifnet *)0; 746df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 7472b758395SGarrett Wollman if (icmpprintfs) { 7482b758395SGarrett Wollman char buf[4 * sizeof "123"]; 7492b758395SGarrett Wollman strcpy(buf, inet_ntoa(ip->ip_dst)); 7502b758395SGarrett Wollman printf("icmp_send dst %s src %s\n", 7512b758395SGarrett Wollman buf, inet_ntoa(ip->ip_src)); 7522b758395SGarrett Wollman } 753df8bae1dSRodney W. Grimes #endif 754bd714208SRuslan Ermilov (void) ip_output(m, opts, rt, 0, NULL); 755df8bae1dSRodney W. Grimes } 756df8bae1dSRodney W. Grimes 757df8bae1dSRodney W. Grimes n_time 758df8bae1dSRodney W. Grimes iptime() 759df8bae1dSRodney W. Grimes { 760df8bae1dSRodney W. Grimes struct timeval atv; 761df8bae1dSRodney W. Grimes u_long t; 762df8bae1dSRodney W. Grimes 76316cd6db0SBill Fumerola getmicrotime(&atv); 764df8bae1dSRodney W. Grimes t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000; 765df8bae1dSRodney W. Grimes return (htonl(t)); 766df8bae1dSRodney W. Grimes } 767df8bae1dSRodney W. Grimes 768b7a44e34SGarrett Wollman #if 1 7695cbf3e08SGarrett Wollman /* 7705cbf3e08SGarrett Wollman * Return the next larger or smaller MTU plateau (table from RFC 1191) 7715cbf3e08SGarrett Wollman * given current value MTU. If DIR is less than zero, a larger plateau 7725cbf3e08SGarrett Wollman * is returned; otherwise, a smaller value is returned. 7735cbf3e08SGarrett Wollman */ 774f708ef1bSPoul-Henning Kamp static int 7755cbf3e08SGarrett Wollman ip_next_mtu(mtu, dir) 7765cbf3e08SGarrett Wollman int mtu; 7775cbf3e08SGarrett Wollman int dir; 7785cbf3e08SGarrett Wollman { 7795cbf3e08SGarrett Wollman static int mtutab[] = { 7805cbf3e08SGarrett Wollman 65535, 32000, 17914, 8166, 4352, 2002, 1492, 1006, 508, 296, 7815cbf3e08SGarrett Wollman 68, 0 7825cbf3e08SGarrett Wollman }; 7835cbf3e08SGarrett Wollman int i; 7845cbf3e08SGarrett Wollman 7855cbf3e08SGarrett Wollman for (i = 0; i < (sizeof mtutab) / (sizeof mtutab[0]); i++) { 7865cbf3e08SGarrett Wollman if (mtu >= mtutab[i]) 7875cbf3e08SGarrett Wollman break; 7885cbf3e08SGarrett Wollman } 7895cbf3e08SGarrett Wollman 7905cbf3e08SGarrett Wollman if (dir < 0) { 7915cbf3e08SGarrett Wollman if (i == 0) { 7925cbf3e08SGarrett Wollman return 0; 7935cbf3e08SGarrett Wollman } else { 7945cbf3e08SGarrett Wollman return mtutab[i - 1]; 7955cbf3e08SGarrett Wollman } 7965cbf3e08SGarrett Wollman } else { 7975cbf3e08SGarrett Wollman if (mtutab[i] == 0) { 7985cbf3e08SGarrett Wollman return 0; 7995cbf3e08SGarrett Wollman } else if(mtu > mtutab[i]) { 8005cbf3e08SGarrett Wollman return mtutab[i]; 8015cbf3e08SGarrett Wollman } else { 8025cbf3e08SGarrett Wollman return mtutab[i + 1]; 8035cbf3e08SGarrett Wollman } 8045cbf3e08SGarrett Wollman } 8055cbf3e08SGarrett Wollman } 806b7a44e34SGarrett Wollman #endif 80751508de1SMatthew Dillon 80851508de1SMatthew Dillon 80951508de1SMatthew Dillon /* 81051508de1SMatthew Dillon * badport_bandlim() - check for ICMP bandwidth limit 81151508de1SMatthew Dillon * 81251508de1SMatthew Dillon * Return 0 if it is ok to send an ICMP error response, -1 if we have 81351508de1SMatthew Dillon * hit our bandwidth limit and it is not ok. 81451508de1SMatthew Dillon * 81551508de1SMatthew Dillon * If icmplim is <= 0, the feature is disabled and 0 is returned. 81651508de1SMatthew Dillon * 81751508de1SMatthew Dillon * For now we separate the TCP and UDP subsystems w/ different 'which' 81851508de1SMatthew Dillon * values. We may eventually remove this separation (and simplify the 81951508de1SMatthew Dillon * code further). 82051508de1SMatthew Dillon * 82151508de1SMatthew Dillon * Note that the printing of the error message is delayed so we can 82251508de1SMatthew Dillon * properly print the icmp error rate that the system was trying to do 82351508de1SMatthew Dillon * (i.e. 22000/100 pps, etc...). This can cause long delays in printing 82451508de1SMatthew Dillon * the 'final' error, but it doesn't make sense to solve the printing 82551508de1SMatthew Dillon * delay with more complex code. 82651508de1SMatthew Dillon */ 82751508de1SMatthew Dillon 82851508de1SMatthew Dillon int 82951508de1SMatthew Dillon badport_bandlim(int which) 83051508de1SMatthew Dillon { 83109f81a46SBosko Milekic static int lticks[BANDLIM_MAX + 1]; 83209f81a46SBosko Milekic static int lpackets[BANDLIM_MAX + 1]; 83351508de1SMatthew Dillon int dticks; 83409f81a46SBosko Milekic const char *bandlimittype[] = { 83509f81a46SBosko Milekic "Limiting icmp unreach response", 83609f81a46SBosko Milekic "Limiting icmp ping response", 837a57815efSBosko Milekic "Limiting icmp tstamp response", 838a57815efSBosko Milekic "Limiting closed port RST response", 839a57815efSBosko Milekic "Limiting open port RST response" 84009f81a46SBosko Milekic }; 84151508de1SMatthew Dillon 84251508de1SMatthew Dillon /* 84351508de1SMatthew Dillon * Return ok status if feature disabled or argument out of 84451508de1SMatthew Dillon * ranage. 84551508de1SMatthew Dillon */ 84651508de1SMatthew Dillon 84709f81a46SBosko Milekic if (icmplim <= 0 || which > BANDLIM_MAX || which < 0) 84851508de1SMatthew Dillon return(0); 84951508de1SMatthew Dillon dticks = ticks - lticks[which]; 85051508de1SMatthew Dillon 85151508de1SMatthew Dillon /* 85251508de1SMatthew Dillon * reset stats when cumulative dt exceeds one second. 85351508de1SMatthew Dillon */ 85451508de1SMatthew Dillon 85551508de1SMatthew Dillon if ((unsigned int)dticks > hz) { 8564f14ee00SDan Moschuk if (lpackets[which] > icmplim && icmplim_output) { 85709f81a46SBosko Milekic printf("%s from %d to %d packets per second\n", 85809f81a46SBosko Milekic bandlimittype[which], 85951508de1SMatthew Dillon lpackets[which], 86051508de1SMatthew Dillon icmplim 86151508de1SMatthew Dillon ); 86251508de1SMatthew Dillon } 86351508de1SMatthew Dillon lticks[which] = ticks; 86451508de1SMatthew Dillon lpackets[which] = 0; 86551508de1SMatthew Dillon } 86651508de1SMatthew Dillon 86751508de1SMatthew Dillon /* 86851508de1SMatthew Dillon * bump packet count 86951508de1SMatthew Dillon */ 87051508de1SMatthew Dillon 87151508de1SMatthew Dillon if (++lpackets[which] > icmplim) { 87251508de1SMatthew Dillon return(-1); 87351508de1SMatthew Dillon } 87451508de1SMatthew Dillon return(0); 87551508de1SMatthew Dillon } 87651508de1SMatthew Dillon 877