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> 499494d596SBrooks Davis #include <net/if_types.h> 50df8bae1dSRodney W. Grimes #include <net/route.h> 51df8bae1dSRodney W. Grimes 525e2d0696SGarrett Wollman #define _IP_VHL 53df8bae1dSRodney W. Grimes #include <netinet/in.h> 54df8bae1dSRodney W. Grimes #include <netinet/in_systm.h> 55df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 56df8bae1dSRodney W. Grimes #include <netinet/ip.h> 57df8bae1dSRodney W. Grimes #include <netinet/ip_icmp.h> 58b5e8ce9fSBruce Evans #include <netinet/ip_var.h> 59df8bae1dSRodney W. Grimes #include <netinet/icmp_var.h> 60df8bae1dSRodney W. Grimes 616a800098SYoshinobu Inoue #ifdef IPSEC 626a800098SYoshinobu Inoue #include <netinet6/ipsec.h> 636a800098SYoshinobu Inoue #include <netkey/key.h> 646a800098SYoshinobu Inoue #endif 656a800098SYoshinobu Inoue 6672a52a35SJonathan Lemon #include <machine/in_cksum.h> 6772a52a35SJonathan Lemon 68df8bae1dSRodney W. Grimes /* 69df8bae1dSRodney W. Grimes * ICMP routines: error generation, receive packet processing, and 70df8bae1dSRodney W. Grimes * routines to turnaround packets back to the originator, and 71df8bae1dSRodney W. Grimes * host table maintenance routines. 72df8bae1dSRodney W. Grimes */ 73df8bae1dSRodney W. Grimes 74f708ef1bSPoul-Henning Kamp static struct icmpstat icmpstat; 75c73d99b5SRuslan Ermilov SYSCTL_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RW, 760312fbe9SPoul-Henning Kamp &icmpstat, icmpstat, ""); 770312fbe9SPoul-Henning Kamp 780312fbe9SPoul-Henning Kamp static int icmpmaskrepl = 0; 790312fbe9SPoul-Henning Kamp SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW, 800312fbe9SPoul-Henning Kamp &icmpmaskrepl, 0, ""); 810312fbe9SPoul-Henning Kamp 8218d3153eSDag-Erling Smørgrav static int drop_redirect = 0; 8318d3153eSDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, drop_redirect, CTLFLAG_RW, 8418d3153eSDag-Erling Smørgrav &drop_redirect, 0, ""); 8518d3153eSDag-Erling Smørgrav 866c3b5f69SDag-Erling Smørgrav static int log_redirect = 0; 876c3b5f69SDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW, 886c3b5f69SDag-Erling Smørgrav &log_redirect, 0, ""); 896c3b5f69SDag-Erling Smørgrav 90173c0f9fSWarner Losh static int icmplim = 200; 9151508de1SMatthew Dillon SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW, 9251508de1SMatthew Dillon &icmplim, 0, ""); 935fce7fc4SMatthew Dillon 944f14ee00SDan Moschuk static int icmplim_output = 1; 954f14ee00SDan Moschuk SYSCTL_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_RW, 964f14ee00SDan Moschuk &icmplim_output, 0, ""); 9751508de1SMatthew Dillon 985fce7fc4SMatthew Dillon /* 995fce7fc4SMatthew Dillon * ICMP broadcast echo sysctl 1005fce7fc4SMatthew Dillon */ 1015fce7fc4SMatthew Dillon 10261a4defdSJoseph Koshy static int icmpbmcastecho = 0; 10318d3153eSDag-Erling Smørgrav SYSCTL_INT(_net_inet_icmp, OID_AUTO, bmcastecho, CTLFLAG_RW, 10418d3153eSDag-Erling Smørgrav &icmpbmcastecho, 0, ""); 1057022ea0aSGarrett Wollman 10651508de1SMatthew Dillon 107df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 108df8bae1dSRodney W. Grimes int icmpprintfs = 0; 109df8bae1dSRodney W. Grimes #endif 110df8bae1dSRodney W. Grimes 1110312fbe9SPoul-Henning Kamp static void icmp_reflect __P((struct mbuf *)); 1120312fbe9SPoul-Henning Kamp static void icmp_send __P((struct mbuf *, struct mbuf *)); 113f708ef1bSPoul-Henning Kamp static int ip_next_mtu __P((int, int)); 1140312fbe9SPoul-Henning Kamp 115df8bae1dSRodney W. Grimes extern struct protosw inetsw[]; 116df8bae1dSRodney W. Grimes 117df8bae1dSRodney W. Grimes /* 118df8bae1dSRodney W. Grimes * Generate an error packet of type error 119df8bae1dSRodney W. Grimes * in response to bad packet ip. 120df8bae1dSRodney W. Grimes */ 121df8bae1dSRodney W. Grimes void 122df8bae1dSRodney W. Grimes icmp_error(n, type, code, dest, destifp) 123df8bae1dSRodney W. Grimes struct mbuf *n; 124df8bae1dSRodney W. Grimes int type, code; 125df8bae1dSRodney W. Grimes n_long dest; 126df8bae1dSRodney W. Grimes struct ifnet *destifp; 127df8bae1dSRodney W. Grimes { 128df8bae1dSRodney W. Grimes register struct ip *oip = mtod(n, struct ip *), *nip; 1295e2d0696SGarrett Wollman register unsigned oiplen = IP_VHL_HL(oip->ip_vhl) << 2; 130df8bae1dSRodney W. Grimes register struct icmp *icp; 131df8bae1dSRodney W. Grimes register struct mbuf *m; 132df8bae1dSRodney W. Grimes unsigned icmplen; 133df8bae1dSRodney W. Grimes 134df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 135df8bae1dSRodney W. Grimes if (icmpprintfs) 136623ae52eSPoul-Henning Kamp printf("icmp_error(%p, %x, %d)\n", oip, type, code); 137df8bae1dSRodney W. Grimes #endif 138df8bae1dSRodney W. Grimes if (type != ICMP_REDIRECT) 139df8bae1dSRodney W. Grimes icmpstat.icps_error++; 140df8bae1dSRodney W. Grimes /* 141df8bae1dSRodney W. Grimes * Don't send error if not the first fragment of message. 142df8bae1dSRodney W. Grimes * Don't error if the old packet protocol was ICMP 143df8bae1dSRodney W. Grimes * error message, only known informational types. 144df8bae1dSRodney W. Grimes */ 145df8bae1dSRodney W. Grimes if (oip->ip_off &~ (IP_MF|IP_DF)) 146df8bae1dSRodney W. Grimes goto freeit; 147df8bae1dSRodney W. Grimes if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT && 148df8bae1dSRodney W. Grimes n->m_len >= oiplen + ICMP_MINLEN && 149df8bae1dSRodney W. Grimes !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) { 150df8bae1dSRodney W. Grimes icmpstat.icps_oldicmp++; 151df8bae1dSRodney W. Grimes goto freeit; 152df8bae1dSRodney W. Grimes } 153df8bae1dSRodney W. Grimes /* Don't send error in response to a multicast or broadcast packet */ 154df8bae1dSRodney W. Grimes if (n->m_flags & (M_BCAST|M_MCAST)) 155df8bae1dSRodney W. Grimes goto freeit; 156df8bae1dSRodney W. Grimes /* 157df8bae1dSRodney W. Grimes * First, formulate icmp message 158df8bae1dSRodney W. Grimes */ 159df8bae1dSRodney W. Grimes m = m_gethdr(M_DONTWAIT, MT_HEADER); 160df8bae1dSRodney W. Grimes if (m == NULL) 161df8bae1dSRodney W. Grimes goto freeit; 1621d027522SRuslan Ermilov icmplen = min(oiplen + 8, oip->ip_len); 163bfef7ed4SIan Dowse if (icmplen < sizeof(struct ip)) 164bfef7ed4SIan Dowse panic("icmp_error: bad length"); 165df8bae1dSRodney W. Grimes m->m_len = icmplen + ICMP_MINLEN; 166df8bae1dSRodney W. Grimes MH_ALIGN(m, m->m_len); 167df8bae1dSRodney W. Grimes icp = mtod(m, struct icmp *); 168df8bae1dSRodney W. Grimes if ((u_int)type > ICMP_MAXTYPE) 169df8bae1dSRodney W. Grimes panic("icmp_error"); 170df8bae1dSRodney W. Grimes icmpstat.icps_outhist[type]++; 171df8bae1dSRodney W. Grimes icp->icmp_type = type; 172df8bae1dSRodney W. Grimes if (type == ICMP_REDIRECT) 173df8bae1dSRodney W. Grimes icp->icmp_gwaddr.s_addr = dest; 174df8bae1dSRodney W. Grimes else { 175df8bae1dSRodney W. Grimes icp->icmp_void = 0; 176df8bae1dSRodney W. Grimes /* 177df8bae1dSRodney W. Grimes * The following assignments assume an overlay with the 178df8bae1dSRodney W. Grimes * zeroed icmp_void field. 179df8bae1dSRodney W. Grimes */ 180df8bae1dSRodney W. Grimes if (type == ICMP_PARAMPROB) { 181df8bae1dSRodney W. Grimes icp->icmp_pptr = code; 182df8bae1dSRodney W. Grimes code = 0; 183df8bae1dSRodney W. Grimes } else if (type == ICMP_UNREACH && 184df8bae1dSRodney W. Grimes code == ICMP_UNREACH_NEEDFRAG && destifp) { 185df8bae1dSRodney W. Grimes icp->icmp_nextmtu = htons(destifp->if_mtu); 186df8bae1dSRodney W. Grimes } 187df8bae1dSRodney W. Grimes } 188df8bae1dSRodney W. Grimes 189df8bae1dSRodney W. Grimes icp->icmp_code = code; 190bfef7ed4SIan Dowse m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip); 191df8bae1dSRodney W. Grimes nip = &icp->icmp_ip; 19204287599SRuslan Ermilov 19304287599SRuslan Ermilov /* 19404287599SRuslan Ermilov * Convert fields to network representation. 19504287599SRuslan Ermilov */ 19604287599SRuslan Ermilov HTONS(nip->ip_len); 19704287599SRuslan Ermilov HTONS(nip->ip_off); 198df8bae1dSRodney W. Grimes 199df8bae1dSRodney W. Grimes /* 200df8bae1dSRodney W. Grimes * Now, copy old ip header (without options) 201df8bae1dSRodney W. Grimes * in front of icmp message. 202df8bae1dSRodney W. Grimes */ 203df8bae1dSRodney W. Grimes if (m->m_data - sizeof(struct ip) < m->m_pktdat) 204df8bae1dSRodney W. Grimes panic("icmp len"); 205df8bae1dSRodney W. Grimes m->m_data -= sizeof(struct ip); 206df8bae1dSRodney W. Grimes m->m_len += sizeof(struct ip); 207df8bae1dSRodney W. Grimes m->m_pkthdr.len = m->m_len; 208df8bae1dSRodney W. Grimes m->m_pkthdr.rcvif = n->m_pkthdr.rcvif; 209df8bae1dSRodney W. Grimes nip = mtod(m, struct ip *); 210df8bae1dSRodney W. Grimes bcopy((caddr_t)oip, (caddr_t)nip, sizeof(struct ip)); 211df8bae1dSRodney W. Grimes nip->ip_len = m->m_len; 2125e2d0696SGarrett Wollman nip->ip_vhl = IP_VHL_BORING; 213df8bae1dSRodney W. Grimes nip->ip_p = IPPROTO_ICMP; 214df8bae1dSRodney W. Grimes nip->ip_tos = 0; 215df8bae1dSRodney W. Grimes icmp_reflect(m); 216df8bae1dSRodney W. Grimes 217df8bae1dSRodney W. Grimes freeit: 218df8bae1dSRodney W. Grimes m_freem(n); 219df8bae1dSRodney W. Grimes } 220df8bae1dSRodney W. Grimes 221df8bae1dSRodney W. Grimes static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET }; 222df8bae1dSRodney W. Grimes static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET }; 223df8bae1dSRodney W. Grimes static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET }; 224df8bae1dSRodney W. Grimes 225df8bae1dSRodney W. Grimes /* 226df8bae1dSRodney W. Grimes * Process a received ICMP message. 227df8bae1dSRodney W. Grimes */ 228df8bae1dSRodney W. Grimes void 229f0ffb944SJulian Elischer icmp_input(m, off) 230df8bae1dSRodney W. Grimes register struct mbuf *m; 231f0ffb944SJulian Elischer int off; 232df8bae1dSRodney W. Grimes { 2336a800098SYoshinobu Inoue int hlen = off; 234df8bae1dSRodney W. Grimes register struct icmp *icp; 235df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 236df8bae1dSRodney W. Grimes int icmplen = ip->ip_len; 237df8bae1dSRodney W. Grimes register int i; 238df8bae1dSRodney W. Grimes struct in_ifaddr *ia; 239b62d102cSBruce Evans void (*ctlfunc) __P((int, struct sockaddr *, void *)); 240df8bae1dSRodney W. Grimes int code; 241df8bae1dSRodney W. Grimes 242df8bae1dSRodney W. Grimes /* 243df8bae1dSRodney W. Grimes * Locate icmp structure in mbuf, and check 244df8bae1dSRodney W. Grimes * that not corrupted and of at least minimum length. 245df8bae1dSRodney W. Grimes */ 246df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 2472b758395SGarrett Wollman if (icmpprintfs) { 2482b758395SGarrett Wollman char buf[4 * sizeof "123"]; 2492b758395SGarrett Wollman strcpy(buf, inet_ntoa(ip->ip_src)); 2502b758395SGarrett Wollman printf("icmp_input from %s to %s, len %d\n", 2512b758395SGarrett Wollman buf, inet_ntoa(ip->ip_dst), icmplen); 2522b758395SGarrett Wollman } 253df8bae1dSRodney W. Grimes #endif 254df8bae1dSRodney W. Grimes if (icmplen < ICMP_MINLEN) { 255df8bae1dSRodney W. Grimes icmpstat.icps_tooshort++; 256df8bae1dSRodney W. Grimes goto freeit; 257df8bae1dSRodney W. Grimes } 258df8bae1dSRodney W. Grimes i = hlen + min(icmplen, ICMP_ADVLENMIN); 259df8bae1dSRodney W. Grimes if (m->m_len < i && (m = m_pullup(m, i)) == 0) { 260df8bae1dSRodney W. Grimes icmpstat.icps_tooshort++; 261df8bae1dSRodney W. Grimes return; 262df8bae1dSRodney W. Grimes } 263df8bae1dSRodney W. Grimes ip = mtod(m, struct ip *); 264df8bae1dSRodney W. Grimes m->m_len -= hlen; 265df8bae1dSRodney W. Grimes m->m_data += hlen; 266df8bae1dSRodney W. Grimes icp = mtod(m, struct icmp *); 267df8bae1dSRodney W. Grimes if (in_cksum(m, icmplen)) { 268df8bae1dSRodney W. Grimes icmpstat.icps_checksum++; 269df8bae1dSRodney W. Grimes goto freeit; 270df8bae1dSRodney W. Grimes } 271df8bae1dSRodney W. Grimes m->m_len += hlen; 272df8bae1dSRodney W. Grimes m->m_data -= hlen; 273df8bae1dSRodney W. Grimes 2746a800098SYoshinobu Inoue if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { 2756a800098SYoshinobu Inoue /* 2766a800098SYoshinobu Inoue * Deliver very specific ICMP type only. 2776a800098SYoshinobu Inoue */ 2786a800098SYoshinobu Inoue switch (icp->icmp_type) { 2796a800098SYoshinobu Inoue case ICMP_UNREACH: 2806a800098SYoshinobu Inoue case ICMP_TIMXCEED: 2816a800098SYoshinobu Inoue break; 2826a800098SYoshinobu Inoue default: 2836a800098SYoshinobu Inoue goto freeit; 2846a800098SYoshinobu Inoue } 2856a800098SYoshinobu Inoue } 2866a800098SYoshinobu Inoue 287df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 288df8bae1dSRodney W. Grimes if (icmpprintfs) 289df8bae1dSRodney W. Grimes printf("icmp_input, type %d code %d\n", icp->icmp_type, 290df8bae1dSRodney W. Grimes icp->icmp_code); 291df8bae1dSRodney W. Grimes #endif 2925b7ee6edSGarrett Wollman 2935b7ee6edSGarrett Wollman /* 2945b7ee6edSGarrett Wollman * Message type specific processing. 2955b7ee6edSGarrett Wollman */ 296df8bae1dSRodney W. Grimes if (icp->icmp_type > ICMP_MAXTYPE) 297df8bae1dSRodney W. Grimes goto raw; 298df8bae1dSRodney W. Grimes icmpstat.icps_inhist[icp->icmp_type]++; 299df8bae1dSRodney W. Grimes code = icp->icmp_code; 300df8bae1dSRodney W. Grimes switch (icp->icmp_type) { 301df8bae1dSRodney W. Grimes 302df8bae1dSRodney W. Grimes case ICMP_UNREACH: 303df8bae1dSRodney W. Grimes switch (code) { 304df8bae1dSRodney W. Grimes case ICMP_UNREACH_NET: 305df8bae1dSRodney W. Grimes case ICMP_UNREACH_HOST: 306df8bae1dSRodney W. Grimes case ICMP_UNREACH_SRCFAIL: 307e4bb5b05SJonathan Lemon case ICMP_UNREACH_NET_UNKNOWN: 308e4bb5b05SJonathan Lemon case ICMP_UNREACH_HOST_UNKNOWN: 309e4bb5b05SJonathan Lemon case ICMP_UNREACH_ISOLATED: 310e4bb5b05SJonathan Lemon case ICMP_UNREACH_TOSNET: 311e4bb5b05SJonathan Lemon case ICMP_UNREACH_TOSHOST: 312e4bb5b05SJonathan Lemon case ICMP_UNREACH_HOST_PRECEDENCE: 313e4bb5b05SJonathan Lemon case ICMP_UNREACH_PRECEDENCE_CUTOFF: 314e4bb5b05SJonathan Lemon code = PRC_UNREACH_NET; 315df8bae1dSRodney W. Grimes break; 316df8bae1dSRodney W. Grimes 317df8bae1dSRodney W. Grimes case ICMP_UNREACH_NEEDFRAG: 318df8bae1dSRodney W. Grimes code = PRC_MSGSIZE; 319df8bae1dSRodney W. Grimes break; 320df8bae1dSRodney W. Grimes 321e4bb5b05SJonathan Lemon /* 322e4bb5b05SJonathan Lemon * RFC 1122, Sections 3.2.2.1 and 4.2.3.9. 323e4bb5b05SJonathan Lemon * Treat subcodes 2,3 as immediate RST 324e4bb5b05SJonathan Lemon */ 325e4bb5b05SJonathan Lemon case ICMP_UNREACH_PROTOCOL: 326e4bb5b05SJonathan Lemon case ICMP_UNREACH_PORT: 327b77d155dSJesper Skriver code = PRC_UNREACH_PORT; 328b11d7a4aSPoul-Henning Kamp break; 32990fcbbd6SPoul-Henning Kamp 33090fcbbd6SPoul-Henning Kamp case ICMP_UNREACH_NET_PROHIB: 33190fcbbd6SPoul-Henning Kamp case ICMP_UNREACH_HOST_PROHIB: 3329c4b2574SPaul Traina case ICMP_UNREACH_FILTER_PROHIB: 33390fcbbd6SPoul-Henning Kamp code = PRC_UNREACH_ADMIN_PROHIB; 334b11d7a4aSPoul-Henning Kamp break; 335b11d7a4aSPoul-Henning Kamp 336df8bae1dSRodney W. Grimes default: 337df8bae1dSRodney W. Grimes goto badcode; 338df8bae1dSRodney W. Grimes } 339df8bae1dSRodney W. Grimes goto deliver; 340df8bae1dSRodney W. Grimes 341df8bae1dSRodney W. Grimes case ICMP_TIMXCEED: 342df8bae1dSRodney W. Grimes if (code > 1) 343df8bae1dSRodney W. Grimes goto badcode; 344df8bae1dSRodney W. Grimes code += PRC_TIMXCEED_INTRANS; 345df8bae1dSRodney W. Grimes goto deliver; 346df8bae1dSRodney W. Grimes 347df8bae1dSRodney W. Grimes case ICMP_PARAMPROB: 348df8bae1dSRodney W. Grimes if (code > 1) 349df8bae1dSRodney W. Grimes goto badcode; 350df8bae1dSRodney W. Grimes code = PRC_PARAMPROB; 351df8bae1dSRodney W. Grimes goto deliver; 352df8bae1dSRodney W. Grimes 353df8bae1dSRodney W. Grimes case ICMP_SOURCEQUENCH: 354df8bae1dSRodney W. Grimes if (code) 355df8bae1dSRodney W. Grimes goto badcode; 356df8bae1dSRodney W. Grimes code = PRC_QUENCH; 357df8bae1dSRodney W. Grimes deliver: 358df8bae1dSRodney W. Grimes /* 359df8bae1dSRodney W. Grimes * Problem with datagram; advise higher level routines. 360df8bae1dSRodney W. Grimes */ 361df8bae1dSRodney W. Grimes if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) || 3625e2d0696SGarrett Wollman IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) { 363df8bae1dSRodney W. Grimes icmpstat.icps_badlen++; 364df8bae1dSRodney W. Grimes goto freeit; 365df8bae1dSRodney W. Grimes } 366df8bae1dSRodney W. Grimes NTOHS(icp->icmp_ip.ip_len); 3675b7ee6edSGarrett Wollman /* Discard ICMP's in response to multicast packets */ 3685b7ee6edSGarrett Wollman if (IN_MULTICAST(ntohl(icp->icmp_ip.ip_dst.s_addr))) 3695b7ee6edSGarrett Wollman goto badcode; 370df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 371df8bae1dSRodney W. Grimes if (icmpprintfs) 372df8bae1dSRodney W. Grimes printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); 373df8bae1dSRodney W. Grimes #endif 374df8bae1dSRodney W. Grimes icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 375b7a44e34SGarrett Wollman #if 1 3765cbf3e08SGarrett Wollman /* 3775cbf3e08SGarrett Wollman * MTU discovery: 3785cbf3e08SGarrett Wollman * If we got a needfrag and there is a host route to the 3795cbf3e08SGarrett Wollman * original destination, and the MTU is not locked, then 3805cbf3e08SGarrett Wollman * set the MTU in the route to the suggested new value 3815cbf3e08SGarrett Wollman * (if given) and then notify as usual. The ULPs will 3825cbf3e08SGarrett Wollman * notice that the MTU has changed and adapt accordingly. 3835cbf3e08SGarrett Wollman * If no new MTU was suggested, then we guess a new one 3845cbf3e08SGarrett Wollman * less than the current value. If the new MTU is 3855cbf3e08SGarrett Wollman * unreasonably small (arbitrarily set at 296), then 3865cbf3e08SGarrett Wollman * we reset the MTU to the interface value and enable the 3875cbf3e08SGarrett Wollman * lock bit, indicating that we are no longer doing MTU 3885cbf3e08SGarrett Wollman * discovery. 3895cbf3e08SGarrett Wollman */ 3905cbf3e08SGarrett Wollman if (code == PRC_MSGSIZE) { 3915cbf3e08SGarrett Wollman struct rtentry *rt; 3925cbf3e08SGarrett Wollman int mtu; 3935cbf3e08SGarrett Wollman 3945cbf3e08SGarrett Wollman rt = rtalloc1((struct sockaddr *)&icmpsrc, 0, 3955cbf3e08SGarrett Wollman RTF_CLONING | RTF_PRCLONING); 3965cbf3e08SGarrett Wollman if (rt && (rt->rt_flags & RTF_HOST) 3975cbf3e08SGarrett Wollman && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { 3985cbf3e08SGarrett Wollman mtu = ntohs(icp->icmp_nextmtu); 3995cbf3e08SGarrett Wollman if (!mtu) 4005cbf3e08SGarrett Wollman mtu = ip_next_mtu(rt->rt_rmx.rmx_mtu, 4015cbf3e08SGarrett Wollman 1); 402be070f43SGarrett Wollman #ifdef DEBUG_MTUDISC 403be070f43SGarrett Wollman printf("MTU for %s reduced to %d\n", 404be070f43SGarrett Wollman inet_ntoa(icmpsrc.sin_addr), mtu); 405be070f43SGarrett Wollman #endif 406be070f43SGarrett Wollman if (mtu < 296) { 407b7a44e34SGarrett Wollman /* rt->rt_rmx.rmx_mtu = 408b7a44e34SGarrett Wollman rt->rt_ifp->if_mtu; */ 4095cbf3e08SGarrett Wollman rt->rt_rmx.rmx_locks |= RTV_MTU; 4105cbf3e08SGarrett Wollman } else if (rt->rt_rmx.rmx_mtu > mtu) { 4115cbf3e08SGarrett Wollman rt->rt_rmx.rmx_mtu = mtu; 4125cbf3e08SGarrett Wollman } 4135cbf3e08SGarrett Wollman } 4145cbf3e08SGarrett Wollman if (rt) 4155cbf3e08SGarrett Wollman RTFREE(rt); 4165cbf3e08SGarrett Wollman } 4175cbf3e08SGarrett Wollman 418b7a44e34SGarrett Wollman #endif 4196a800098SYoshinobu Inoue /* 4206a800098SYoshinobu Inoue * XXX if the packet contains [IPv4 AH TCP], we can't make a 4216a800098SYoshinobu Inoue * notification to TCP layer. 4226a800098SYoshinobu Inoue */ 423623ae52eSPoul-Henning Kamp ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput; 424623ae52eSPoul-Henning Kamp if (ctlfunc) 425df8bae1dSRodney W. Grimes (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, 426b62d102cSBruce Evans (void *)&icp->icmp_ip); 427df8bae1dSRodney W. Grimes break; 428df8bae1dSRodney W. Grimes 429df8bae1dSRodney W. Grimes badcode: 430df8bae1dSRodney W. Grimes icmpstat.icps_badcode++; 431df8bae1dSRodney W. Grimes break; 432df8bae1dSRodney W. Grimes 433df8bae1dSRodney W. Grimes case ICMP_ECHO: 4347022ea0aSGarrett Wollman if (!icmpbmcastecho 435d311884fSDavid Greenman && (m->m_flags & (M_MCAST | M_BCAST)) != 0) { 4367022ea0aSGarrett Wollman icmpstat.icps_bmcastecho++; 4377022ea0aSGarrett Wollman break; 4387022ea0aSGarrett Wollman } 439df8bae1dSRodney W. Grimes icp->icmp_type = ICMP_ECHOREPLY; 440a57815efSBosko Milekic if (badport_bandlim(BANDLIM_ICMP_ECHO) < 0) 44109f81a46SBosko Milekic goto freeit; 44209f81a46SBosko Milekic else 443df8bae1dSRodney W. Grimes goto reflect; 444df8bae1dSRodney W. Grimes 445df8bae1dSRodney W. Grimes case ICMP_TSTAMP: 446fe0fb8abSGarrett Wollman if (!icmpbmcastecho 447d311884fSDavid Greenman && (m->m_flags & (M_MCAST | M_BCAST)) != 0) { 448fe0fb8abSGarrett Wollman icmpstat.icps_bmcasttstamp++; 449fe0fb8abSGarrett Wollman break; 450fe0fb8abSGarrett Wollman } 451df8bae1dSRodney W. Grimes if (icmplen < ICMP_TSLEN) { 452df8bae1dSRodney W. Grimes icmpstat.icps_badlen++; 453df8bae1dSRodney W. Grimes break; 454df8bae1dSRodney W. Grimes } 455df8bae1dSRodney W. Grimes icp->icmp_type = ICMP_TSTAMPREPLY; 456df8bae1dSRodney W. Grimes icp->icmp_rtime = iptime(); 457df8bae1dSRodney W. Grimes icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */ 458a57815efSBosko Milekic if (badport_bandlim(BANDLIM_ICMP_TSTAMP) < 0) 45909f81a46SBosko Milekic goto freeit; 46009f81a46SBosko Milekic else 461df8bae1dSRodney W. Grimes goto reflect; 462df8bae1dSRodney W. Grimes 463df8bae1dSRodney W. Grimes case ICMP_MASKREQ: 464df8bae1dSRodney W. Grimes if (icmpmaskrepl == 0) 465df8bae1dSRodney W. Grimes break; 466df8bae1dSRodney W. Grimes /* 467df8bae1dSRodney W. Grimes * We are not able to respond with all ones broadcast 468df8bae1dSRodney W. Grimes * unless we receive it over a point-to-point interface. 469df8bae1dSRodney W. Grimes */ 470df8bae1dSRodney W. Grimes if (icmplen < ICMP_MASKLEN) 471df8bae1dSRodney W. Grimes break; 472df8bae1dSRodney W. Grimes switch (ip->ip_dst.s_addr) { 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes case INADDR_BROADCAST: 475df8bae1dSRodney W. Grimes case INADDR_ANY: 476df8bae1dSRodney W. Grimes icmpdst.sin_addr = ip->ip_src; 477df8bae1dSRodney W. Grimes break; 478df8bae1dSRodney W. Grimes 479df8bae1dSRodney W. Grimes default: 480df8bae1dSRodney W. Grimes icmpdst.sin_addr = ip->ip_dst; 481df8bae1dSRodney W. Grimes } 482df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)ifaof_ifpforaddr( 483df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif); 484df8bae1dSRodney W. Grimes if (ia == 0) 485df8bae1dSRodney W. Grimes break; 4867e6f7714SPoul-Henning Kamp if (ia->ia_ifp == 0) 4877e6f7714SPoul-Henning Kamp break; 488df8bae1dSRodney W. Grimes icp->icmp_type = ICMP_MASKREPLY; 489df8bae1dSRodney W. Grimes icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr; 490df8bae1dSRodney W. Grimes if (ip->ip_src.s_addr == 0) { 491df8bae1dSRodney W. Grimes if (ia->ia_ifp->if_flags & IFF_BROADCAST) 492df8bae1dSRodney W. Grimes ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; 493df8bae1dSRodney W. Grimes else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) 494df8bae1dSRodney W. Grimes ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; 495df8bae1dSRodney W. Grimes } 496df8bae1dSRodney W. Grimes reflect: 497df8bae1dSRodney W. Grimes ip->ip_len += hlen; /* since ip_input deducts this */ 498df8bae1dSRodney W. Grimes icmpstat.icps_reflect++; 499df8bae1dSRodney W. Grimes icmpstat.icps_outhist[icp->icmp_type]++; 500df8bae1dSRodney W. Grimes icmp_reflect(m); 501df8bae1dSRodney W. Grimes return; 502df8bae1dSRodney W. Grimes 503df8bae1dSRodney W. Grimes case ICMP_REDIRECT: 50418d3153eSDag-Erling Smørgrav if (log_redirect) { 50518d3153eSDag-Erling Smørgrav u_long src, dst, gw; 50618d3153eSDag-Erling Smørgrav 50718d3153eSDag-Erling Smørgrav src = ntohl(ip->ip_src.s_addr); 50818d3153eSDag-Erling Smørgrav dst = ntohl(icp->icmp_ip.ip_dst.s_addr); 50918d3153eSDag-Erling Smørgrav gw = ntohl(icp->icmp_gwaddr.s_addr); 51018d3153eSDag-Erling Smørgrav printf("icmp redirect from %d.%d.%d.%d: " 51118d3153eSDag-Erling Smørgrav "%d.%d.%d.%d => %d.%d.%d.%d\n", 51218d3153eSDag-Erling Smørgrav (int)(src >> 24), (int)((src >> 16) & 0xff), 51318d3153eSDag-Erling Smørgrav (int)((src >> 8) & 0xff), (int)(src & 0xff), 51418d3153eSDag-Erling Smørgrav (int)(dst >> 24), (int)((dst >> 16) & 0xff), 51518d3153eSDag-Erling Smørgrav (int)((dst >> 8) & 0xff), (int)(dst & 0xff), 51618d3153eSDag-Erling Smørgrav (int)(gw >> 24), (int)((gw >> 16) & 0xff), 51718d3153eSDag-Erling Smørgrav (int)((gw >> 8) & 0xff), (int)(gw & 0xff)); 51818d3153eSDag-Erling Smørgrav } 51918d3153eSDag-Erling Smørgrav if (drop_redirect) 52018d3153eSDag-Erling Smørgrav break; 521df8bae1dSRodney W. Grimes if (code > 3) 522df8bae1dSRodney W. Grimes goto badcode; 523df8bae1dSRodney W. Grimes if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) || 5245e2d0696SGarrett Wollman IP_VHL_HL(icp->icmp_ip.ip_vhl) < (sizeof(struct ip) >> 2)) { 525df8bae1dSRodney W. Grimes icmpstat.icps_badlen++; 526df8bae1dSRodney W. Grimes break; 527df8bae1dSRodney W. Grimes } 528df8bae1dSRodney W. Grimes /* 529df8bae1dSRodney W. Grimes * Short circuit routing redirects to force 530df8bae1dSRodney W. Grimes * immediate change in the kernel's routing 531df8bae1dSRodney W. Grimes * tables. The message is also handed to anyone 532df8bae1dSRodney W. Grimes * listening on a raw socket (e.g. the routing 533df8bae1dSRodney W. Grimes * daemon for use in updating its tables). 534df8bae1dSRodney W. Grimes */ 535df8bae1dSRodney W. Grimes icmpgw.sin_addr = ip->ip_src; 536df8bae1dSRodney W. Grimes icmpdst.sin_addr = icp->icmp_gwaddr; 537df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 5382b758395SGarrett Wollman if (icmpprintfs) { 5392b758395SGarrett Wollman char buf[4 * sizeof "123"]; 5402b758395SGarrett Wollman strcpy(buf, inet_ntoa(icp->icmp_ip.ip_dst)); 5412b758395SGarrett Wollman 5422b758395SGarrett Wollman printf("redirect dst %s to %s\n", 5432b758395SGarrett Wollman buf, inet_ntoa(icp->icmp_gwaddr)); 5442b758395SGarrett Wollman } 545df8bae1dSRodney W. Grimes #endif 546df8bae1dSRodney W. Grimes icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 547df8bae1dSRodney W. Grimes rtredirect((struct sockaddr *)&icmpsrc, 548df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpdst, 549df8bae1dSRodney W. Grimes (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST, 550df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpgw, (struct rtentry **)0); 551df8bae1dSRodney W. Grimes pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&icmpsrc); 5526a800098SYoshinobu Inoue #ifdef IPSEC 5536a800098SYoshinobu Inoue key_sa_routechange((struct sockaddr *)&icmpsrc); 5546a800098SYoshinobu Inoue #endif 555df8bae1dSRodney W. Grimes break; 556df8bae1dSRodney W. Grimes 557df8bae1dSRodney W. Grimes /* 558df8bae1dSRodney W. Grimes * No kernel processing for the following; 559df8bae1dSRodney W. Grimes * just fall through to send to raw listener. 560df8bae1dSRodney W. Grimes */ 561df8bae1dSRodney W. Grimes case ICMP_ECHOREPLY: 562df8bae1dSRodney W. Grimes case ICMP_ROUTERADVERT: 563df8bae1dSRodney W. Grimes case ICMP_ROUTERSOLICIT: 564df8bae1dSRodney W. Grimes case ICMP_TSTAMPREPLY: 565df8bae1dSRodney W. Grimes case ICMP_IREQREPLY: 566df8bae1dSRodney W. Grimes case ICMP_MASKREPLY: 567df8bae1dSRodney W. Grimes default: 568df8bae1dSRodney W. Grimes break; 569df8bae1dSRodney W. Grimes } 570df8bae1dSRodney W. Grimes 571df8bae1dSRodney W. Grimes raw: 572f0ffb944SJulian Elischer rip_input(m, off); 573df8bae1dSRodney W. Grimes return; 574df8bae1dSRodney W. Grimes 575df8bae1dSRodney W. Grimes freeit: 576df8bae1dSRodney W. Grimes m_freem(m); 577df8bae1dSRodney W. Grimes } 578df8bae1dSRodney W. Grimes 579df8bae1dSRodney W. Grimes /* 580df8bae1dSRodney W. Grimes * Reflect the ip packet back to the source 581df8bae1dSRodney W. Grimes */ 5820312fbe9SPoul-Henning Kamp static void 583df8bae1dSRodney W. Grimes icmp_reflect(m) 584df8bae1dSRodney W. Grimes struct mbuf *m; 585df8bae1dSRodney W. Grimes { 586ca925d9cSJonathan Lemon struct ip *ip = mtod(m, struct ip *); 587ca925d9cSJonathan Lemon struct ifaddr *ifa; 588ca925d9cSJonathan Lemon struct in_ifaddr *ia; 589df8bae1dSRodney W. Grimes struct in_addr t; 590b5e8ce9fSBruce Evans struct mbuf *opts = 0; 5915e2d0696SGarrett Wollman int optlen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof(struct ip); 592df8bae1dSRodney W. Grimes 593df8bae1dSRodney W. Grimes if (!in_canforward(ip->ip_src) && 594df8bae1dSRodney W. Grimes ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) != 595df8bae1dSRodney W. Grimes (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) { 596df8bae1dSRodney W. Grimes m_freem(m); /* Bad return address */ 597df8bae1dSRodney W. Grimes goto done; /* Ip_output() will check for broadcast */ 598df8bae1dSRodney W. Grimes } 599df8bae1dSRodney W. Grimes t = ip->ip_dst; 600df8bae1dSRodney W. Grimes ip->ip_dst = ip->ip_src; 601df8bae1dSRodney W. Grimes /* 602df8bae1dSRodney W. Grimes * If the incoming packet was addressed directly to us, 603df8bae1dSRodney W. Grimes * use dst as the src for the reply. Otherwise (broadcast 604df8bae1dSRodney W. Grimes * or anonymous), use the address which corresponds 605df8bae1dSRodney W. Grimes * to the incoming interface. 606df8bae1dSRodney W. Grimes */ 607ca925d9cSJonathan Lemon LIST_FOREACH(ia, INADDR_HASH(t.s_addr), ia_hash) 608df8bae1dSRodney W. Grimes if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) 609ca925d9cSJonathan Lemon goto match; 610ca925d9cSJonathan Lemon if (m->m_pkthdr.rcvif->if_flags & IFF_BROADCAST) { 611ca925d9cSJonathan Lemon TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) { 612ca925d9cSJonathan Lemon if (ifa->ifa_addr->sa_family != AF_INET) 613ca925d9cSJonathan Lemon continue; 614ca925d9cSJonathan Lemon ia = ifatoia(ifa); 615ca925d9cSJonathan Lemon if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == 616ca925d9cSJonathan Lemon t.s_addr) 617ca925d9cSJonathan Lemon goto match; 618df8bae1dSRodney W. Grimes } 619ca925d9cSJonathan Lemon } 620ca925d9cSJonathan Lemon KASSERT(m->m_pkthdr.rcvif != NULL, ("icmp_reflect: NULL rcvif")); 621df8bae1dSRodney W. Grimes icmpdst.sin_addr = t; 622df8bae1dSRodney W. Grimes ia = (struct in_ifaddr *)ifaof_ifpforaddr( 623df8bae1dSRodney W. Grimes (struct sockaddr *)&icmpdst, m->m_pkthdr.rcvif); 624df8bae1dSRodney W. Grimes /* 625df8bae1dSRodney W. Grimes * The following happens if the packet was not addressed to us, 626df8bae1dSRodney W. Grimes * and was received on an interface with no IP address. 627df8bae1dSRodney W. Grimes */ 628ca925d9cSJonathan Lemon if (ia == NULL) 629fc2ffbe6SPoul-Henning Kamp ia = TAILQ_FIRST(&in_ifaddrhead); 630ca925d9cSJonathan Lemon match: 631df8bae1dSRodney W. Grimes t = IA_SIN(ia)->sin_addr; 632df8bae1dSRodney W. Grimes ip->ip_src = t; 6338ce3f3ddSRuslan Ermilov ip->ip_ttl = ip_defttl; 634df8bae1dSRodney W. Grimes 635df8bae1dSRodney W. Grimes if (optlen > 0) { 636df8bae1dSRodney W. Grimes register u_char *cp; 637df8bae1dSRodney W. Grimes int opt, cnt; 638df8bae1dSRodney W. Grimes u_int len; 639df8bae1dSRodney W. Grimes 640df8bae1dSRodney W. Grimes /* 641df8bae1dSRodney W. Grimes * Retrieve any source routing from the incoming packet; 642df8bae1dSRodney W. Grimes * add on any record-route or timestamp options. 643df8bae1dSRodney W. Grimes */ 644df8bae1dSRodney W. Grimes cp = (u_char *) (ip + 1); 645df8bae1dSRodney W. Grimes if ((opts = ip_srcroute()) == 0 && 646df8bae1dSRodney W. Grimes (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) { 647df8bae1dSRodney W. Grimes opts->m_len = sizeof(struct in_addr); 648df8bae1dSRodney W. Grimes mtod(opts, struct in_addr *)->s_addr = 0; 649df8bae1dSRodney W. Grimes } 650df8bae1dSRodney W. Grimes if (opts) { 651df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 652df8bae1dSRodney W. Grimes if (icmpprintfs) 653df8bae1dSRodney W. Grimes printf("icmp_reflect optlen %d rt %d => ", 654df8bae1dSRodney W. Grimes optlen, opts->m_len); 655df8bae1dSRodney W. Grimes #endif 656df8bae1dSRodney W. Grimes for (cnt = optlen; cnt > 0; cnt -= len, cp += len) { 657df8bae1dSRodney W. Grimes opt = cp[IPOPT_OPTVAL]; 658df8bae1dSRodney W. Grimes if (opt == IPOPT_EOL) 659df8bae1dSRodney W. Grimes break; 660df8bae1dSRodney W. Grimes if (opt == IPOPT_NOP) 661df8bae1dSRodney W. Grimes len = 1; 662df8bae1dSRodney W. Grimes else { 663707d00a3SJonathan Lemon if (cnt < IPOPT_OLEN + sizeof(*cp)) 664707d00a3SJonathan Lemon break; 665df8bae1dSRodney W. Grimes len = cp[IPOPT_OLEN]; 666707d00a3SJonathan Lemon if (len < IPOPT_OLEN + sizeof(*cp) || 667707d00a3SJonathan Lemon len > cnt) 668df8bae1dSRodney W. Grimes break; 669df8bae1dSRodney W. Grimes } 670df8bae1dSRodney W. Grimes /* 671df8bae1dSRodney W. Grimes * Should check for overflow, but it "can't happen" 672df8bae1dSRodney W. Grimes */ 673df8bae1dSRodney W. Grimes if (opt == IPOPT_RR || opt == IPOPT_TS || 674df8bae1dSRodney W. Grimes opt == IPOPT_SECURITY) { 675df8bae1dSRodney W. Grimes bcopy((caddr_t)cp, 676df8bae1dSRodney W. Grimes mtod(opts, caddr_t) + opts->m_len, len); 677df8bae1dSRodney W. Grimes opts->m_len += len; 678df8bae1dSRodney W. Grimes } 679df8bae1dSRodney W. Grimes } 680df8bae1dSRodney W. Grimes /* Terminate & pad, if necessary */ 681623ae52eSPoul-Henning Kamp cnt = opts->m_len % 4; 682623ae52eSPoul-Henning Kamp if (cnt) { 683df8bae1dSRodney W. Grimes for (; cnt < 4; cnt++) { 684df8bae1dSRodney W. Grimes *(mtod(opts, caddr_t) + opts->m_len) = 685df8bae1dSRodney W. Grimes IPOPT_EOL; 686df8bae1dSRodney W. Grimes opts->m_len++; 687df8bae1dSRodney W. Grimes } 688df8bae1dSRodney W. Grimes } 689df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 690df8bae1dSRodney W. Grimes if (icmpprintfs) 691df8bae1dSRodney W. Grimes printf("%d\n", opts->m_len); 692df8bae1dSRodney W. Grimes #endif 693df8bae1dSRodney W. Grimes } 694df8bae1dSRodney W. Grimes /* 695df8bae1dSRodney W. Grimes * Now strip out original options by copying rest of first 696df8bae1dSRodney W. Grimes * mbuf's data back, and adjust the IP length. 697df8bae1dSRodney W. Grimes */ 698df8bae1dSRodney W. Grimes ip->ip_len -= optlen; 6995e2d0696SGarrett Wollman ip->ip_vhl = IP_VHL_BORING; 700df8bae1dSRodney W. Grimes m->m_len -= optlen; 701df8bae1dSRodney W. Grimes if (m->m_flags & M_PKTHDR) 702df8bae1dSRodney W. Grimes m->m_pkthdr.len -= optlen; 703df8bae1dSRodney W. Grimes optlen += sizeof(struct ip); 704df8bae1dSRodney W. Grimes bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1), 705df8bae1dSRodney W. Grimes (unsigned)(m->m_len - sizeof(struct ip))); 706df8bae1dSRodney W. Grimes } 707df8bae1dSRodney W. Grimes m->m_flags &= ~(M_BCAST|M_MCAST); 708df8bae1dSRodney W. Grimes icmp_send(m, opts); 709df8bae1dSRodney W. Grimes done: 710df8bae1dSRodney W. Grimes if (opts) 711df8bae1dSRodney W. Grimes (void)m_free(opts); 712df8bae1dSRodney W. Grimes } 713df8bae1dSRodney W. Grimes 714df8bae1dSRodney W. Grimes /* 715df8bae1dSRodney W. Grimes * Send an icmp packet back to the ip level, 716df8bae1dSRodney W. Grimes * after supplying a checksum. 717df8bae1dSRodney W. Grimes */ 7180312fbe9SPoul-Henning Kamp static void 719df8bae1dSRodney W. Grimes icmp_send(m, opts) 720df8bae1dSRodney W. Grimes register struct mbuf *m; 721df8bae1dSRodney W. Grimes struct mbuf *opts; 722df8bae1dSRodney W. Grimes { 723df8bae1dSRodney W. Grimes register struct ip *ip = mtod(m, struct ip *); 724df8bae1dSRodney W. Grimes register int hlen; 725df8bae1dSRodney W. Grimes register struct icmp *icp; 726d3d20ad1SGarrett Wollman struct route ro; 727df8bae1dSRodney W. Grimes 7285e2d0696SGarrett Wollman hlen = IP_VHL_HL(ip->ip_vhl) << 2; 729df8bae1dSRodney W. Grimes m->m_data += hlen; 730df8bae1dSRodney W. Grimes m->m_len -= hlen; 731df8bae1dSRodney W. Grimes icp = mtod(m, struct icmp *); 732df8bae1dSRodney W. Grimes icp->icmp_cksum = 0; 733df8bae1dSRodney W. Grimes icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); 734df8bae1dSRodney W. Grimes m->m_data -= hlen; 735df8bae1dSRodney W. Grimes m->m_len += hlen; 73694446a2eSArchie Cobbs m->m_pkthdr.rcvif = (struct ifnet *)0; 737df8bae1dSRodney W. Grimes #ifdef ICMPPRINTFS 7382b758395SGarrett Wollman if (icmpprintfs) { 7392b758395SGarrett Wollman char buf[4 * sizeof "123"]; 7402b758395SGarrett Wollman strcpy(buf, inet_ntoa(ip->ip_dst)); 7412b758395SGarrett Wollman printf("icmp_send dst %s src %s\n", 7422b758395SGarrett Wollman buf, inet_ntoa(ip->ip_src)); 7432b758395SGarrett Wollman } 744df8bae1dSRodney W. Grimes #endif 745d3d20ad1SGarrett Wollman bzero(&ro, sizeof ro); 746d3d20ad1SGarrett Wollman (void) ip_output(m, opts, &ro, 0, NULL); 747d3d20ad1SGarrett Wollman if (ro.ro_rt) 748d3d20ad1SGarrett Wollman RTFREE(ro.ro_rt); 749df8bae1dSRodney W. Grimes } 750df8bae1dSRodney W. Grimes 751df8bae1dSRodney W. Grimes n_time 752df8bae1dSRodney W. Grimes iptime() 753df8bae1dSRodney W. Grimes { 754df8bae1dSRodney W. Grimes struct timeval atv; 755df8bae1dSRodney W. Grimes u_long t; 756df8bae1dSRodney W. Grimes 75716cd6db0SBill Fumerola getmicrotime(&atv); 758df8bae1dSRodney W. Grimes t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000; 759df8bae1dSRodney W. Grimes return (htonl(t)); 760df8bae1dSRodney W. Grimes } 761df8bae1dSRodney W. Grimes 762b7a44e34SGarrett Wollman #if 1 7635cbf3e08SGarrett Wollman /* 7645cbf3e08SGarrett Wollman * Return the next larger or smaller MTU plateau (table from RFC 1191) 7655cbf3e08SGarrett Wollman * given current value MTU. If DIR is less than zero, a larger plateau 7665cbf3e08SGarrett Wollman * is returned; otherwise, a smaller value is returned. 7675cbf3e08SGarrett Wollman */ 768f708ef1bSPoul-Henning Kamp static int 7695cbf3e08SGarrett Wollman ip_next_mtu(mtu, dir) 7705cbf3e08SGarrett Wollman int mtu; 7715cbf3e08SGarrett Wollman int dir; 7725cbf3e08SGarrett Wollman { 7735cbf3e08SGarrett Wollman static int mtutab[] = { 7745cbf3e08SGarrett Wollman 65535, 32000, 17914, 8166, 4352, 2002, 1492, 1006, 508, 296, 7755cbf3e08SGarrett Wollman 68, 0 7765cbf3e08SGarrett Wollman }; 7775cbf3e08SGarrett Wollman int i; 7785cbf3e08SGarrett Wollman 7795cbf3e08SGarrett Wollman for (i = 0; i < (sizeof mtutab) / (sizeof mtutab[0]); i++) { 7805cbf3e08SGarrett Wollman if (mtu >= mtutab[i]) 7815cbf3e08SGarrett Wollman break; 7825cbf3e08SGarrett Wollman } 7835cbf3e08SGarrett Wollman 7845cbf3e08SGarrett Wollman if (dir < 0) { 7855cbf3e08SGarrett Wollman if (i == 0) { 7865cbf3e08SGarrett Wollman return 0; 7875cbf3e08SGarrett Wollman } else { 7885cbf3e08SGarrett Wollman return mtutab[i - 1]; 7895cbf3e08SGarrett Wollman } 7905cbf3e08SGarrett Wollman } else { 7915cbf3e08SGarrett Wollman if (mtutab[i] == 0) { 7925cbf3e08SGarrett Wollman return 0; 7935cbf3e08SGarrett Wollman } else if(mtu > mtutab[i]) { 7945cbf3e08SGarrett Wollman return mtutab[i]; 7955cbf3e08SGarrett Wollman } else { 7965cbf3e08SGarrett Wollman return mtutab[i + 1]; 7975cbf3e08SGarrett Wollman } 7985cbf3e08SGarrett Wollman } 7995cbf3e08SGarrett Wollman } 800b7a44e34SGarrett Wollman #endif 80151508de1SMatthew Dillon 80251508de1SMatthew Dillon 80351508de1SMatthew Dillon /* 80451508de1SMatthew Dillon * badport_bandlim() - check for ICMP bandwidth limit 80551508de1SMatthew Dillon * 80651508de1SMatthew Dillon * Return 0 if it is ok to send an ICMP error response, -1 if we have 80751508de1SMatthew Dillon * hit our bandwidth limit and it is not ok. 80851508de1SMatthew Dillon * 80951508de1SMatthew Dillon * If icmplim is <= 0, the feature is disabled and 0 is returned. 81051508de1SMatthew Dillon * 81151508de1SMatthew Dillon * For now we separate the TCP and UDP subsystems w/ different 'which' 81251508de1SMatthew Dillon * values. We may eventually remove this separation (and simplify the 81351508de1SMatthew Dillon * code further). 81451508de1SMatthew Dillon * 81551508de1SMatthew Dillon * Note that the printing of the error message is delayed so we can 81651508de1SMatthew Dillon * properly print the icmp error rate that the system was trying to do 81751508de1SMatthew Dillon * (i.e. 22000/100 pps, etc...). This can cause long delays in printing 81851508de1SMatthew Dillon * the 'final' error, but it doesn't make sense to solve the printing 81951508de1SMatthew Dillon * delay with more complex code. 82051508de1SMatthew Dillon */ 82151508de1SMatthew Dillon 82251508de1SMatthew Dillon int 82351508de1SMatthew Dillon badport_bandlim(int which) 82451508de1SMatthew Dillon { 82509f81a46SBosko Milekic static int lticks[BANDLIM_MAX + 1]; 82609f81a46SBosko Milekic static int lpackets[BANDLIM_MAX + 1]; 82751508de1SMatthew Dillon int dticks; 82809f81a46SBosko Milekic const char *bandlimittype[] = { 82909f81a46SBosko Milekic "Limiting icmp unreach response", 83009f81a46SBosko Milekic "Limiting icmp ping response", 831a57815efSBosko Milekic "Limiting icmp tstamp response", 832a57815efSBosko Milekic "Limiting closed port RST response", 833a57815efSBosko Milekic "Limiting open port RST response" 83409f81a46SBosko Milekic }; 83551508de1SMatthew Dillon 83651508de1SMatthew Dillon /* 83751508de1SMatthew Dillon * Return ok status if feature disabled or argument out of 83851508de1SMatthew Dillon * ranage. 83951508de1SMatthew Dillon */ 84051508de1SMatthew Dillon 84109f81a46SBosko Milekic if (icmplim <= 0 || which > BANDLIM_MAX || which < 0) 84251508de1SMatthew Dillon return(0); 84351508de1SMatthew Dillon dticks = ticks - lticks[which]; 84451508de1SMatthew Dillon 84551508de1SMatthew Dillon /* 84651508de1SMatthew Dillon * reset stats when cumulative dt exceeds one second. 84751508de1SMatthew Dillon */ 84851508de1SMatthew Dillon 84951508de1SMatthew Dillon if ((unsigned int)dticks > hz) { 8504f14ee00SDan Moschuk if (lpackets[which] > icmplim && icmplim_output) { 85109f81a46SBosko Milekic printf("%s from %d to %d packets per second\n", 85209f81a46SBosko Milekic bandlimittype[which], 85351508de1SMatthew Dillon lpackets[which], 85451508de1SMatthew Dillon icmplim 85551508de1SMatthew Dillon ); 85651508de1SMatthew Dillon } 85751508de1SMatthew Dillon lticks[which] = ticks; 85851508de1SMatthew Dillon lpackets[which] = 0; 85951508de1SMatthew Dillon } 86051508de1SMatthew Dillon 86151508de1SMatthew Dillon /* 86251508de1SMatthew Dillon * bump packet count 86351508de1SMatthew Dillon */ 86451508de1SMatthew Dillon 86551508de1SMatthew Dillon if (++lpackets[which] > icmplim) { 86651508de1SMatthew Dillon return(-1); 86751508de1SMatthew Dillon } 86851508de1SMatthew Dillon return(0); 86951508de1SMatthew Dillon } 87051508de1SMatthew Dillon 871