123182Smckusick /* 223182Smckusick * Copyright (c) 1982 Regents of the University of California. 323182Smckusick * All rights reserved. The Berkeley software License Agreement 423182Smckusick * specifies the terms and conditions for redistribution. 523182Smckusick * 6*28926Skarels * @(#)ip_icmp.c 6.20 (Berkeley) 05/31/86 723182Smckusick */ 84785Swnj 917059Sbloom #include "param.h" 1017059Sbloom #include "systm.h" 1117059Sbloom #include "mbuf.h" 1217059Sbloom #include "protosw.h" 1317059Sbloom #include "socket.h" 1417059Sbloom #include "time.h" 1517059Sbloom #include "kernel.h" 168694Sroot 178694Sroot #include "../net/route.h" 1818373Skarels #include "../net/if.h" 1910891Ssam 2017059Sbloom #include "in.h" 2117059Sbloom #include "in_systm.h" 2218373Skarels #include "in_var.h" 2317059Sbloom #include "ip.h" 2417059Sbloom #include "ip_icmp.h" 2517059Sbloom #include "icmp_var.h" 264785Swnj 2715027Smckusick #ifdef ICMPPRINTFS 284785Swnj /* 294785Swnj * ICMP routines: error generation, receive packet processing, and 304785Swnj * routines to turnaround packets back to the originator, and 314785Swnj * host table maintenance routines. 324785Swnj */ 336608Ssam int icmpprintfs = 0; 3415027Smckusick #endif 354785Swnj 364785Swnj /* 376583Ssam * Generate an error packet of type error 386583Ssam * in response to bad packet ip. 394785Swnj */ 4026034Skarels /*VARARGS4*/ 4126034Skarels icmp_error(oip, type, code, ifp, dest) 424785Swnj struct ip *oip; 4312764Ssam int type, code; 4426034Skarels struct ifnet *ifp; 4524811Skarels struct in_addr dest; 464785Swnj { 476583Ssam register unsigned oiplen = oip->ip_hl << 2; 486583Ssam register struct icmp *icp; 494785Swnj struct mbuf *m; 504785Swnj struct ip *nip; 5126383Skarels unsigned icmplen; 524785Swnj 5315027Smckusick #ifdef ICMPPRINTFS 546590Ssam if (icmpprintfs) 556590Ssam printf("icmp_error(%x, %d, %d)\n", oip, type, code); 5615027Smckusick #endif 5726034Skarels if (type != ICMP_REDIRECT) 5826034Skarels icmpstat.icps_error++; 594785Swnj /* 6024811Skarels * Don't send error if not the first fragment of message. 6124811Skarels * Don't EVER error if the old packet protocol was ICMP. 6224811Skarels * (Could do ECHO, etc, but not error indications.) 634785Swnj */ 6424811Skarels if (oip->ip_off &~ (IP_MF|IP_DF)) 654785Swnj goto free; 6624811Skarels if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT) { 6711532Ssam icmpstat.icps_oldicmp++; 6811532Ssam goto free; 6911532Ssam } 704785Swnj 714785Swnj /* 726583Ssam * First, formulate icmp message 734785Swnj */ 749640Ssam m = m_get(M_DONTWAIT, MT_HEADER); 7511532Ssam if (m == NULL) 764785Swnj goto free; 7724811Skarels icmplen = oiplen + MIN(8, oip->ip_len); 7824811Skarels m->m_len = icmplen + ICMP_MINLEN; 796583Ssam m->m_off = MMAXOFF - m->m_len; 806583Ssam icp = mtod(m, struct icmp *); 8124811Skarels if ((u_int)type > ICMP_MAXTYPE) 8211532Ssam panic("icmp_error"); 8311532Ssam icmpstat.icps_outhist[type]++; 844785Swnj icp->icmp_type = type; 8524811Skarels if (type == ICMP_REDIRECT) 8624811Skarels icp->icmp_gwaddr = dest; 8724811Skarels else 8824811Skarels icp->icmp_void = 0; 894785Swnj if (type == ICMP_PARAMPROB) { 904785Swnj icp->icmp_pptr = code; 916583Ssam code = 0; 926583Ssam } 936583Ssam icp->icmp_code = code; 9424811Skarels bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen); 956590Ssam nip = &icp->icmp_ip; 966590Ssam nip->ip_len += oiplen; 976590Ssam nip->ip_len = htons((u_short)nip->ip_len); 984785Swnj 994785Swnj /* 10027063Skarels * Now, copy old ip header in front of icmp message. 1014785Swnj */ 10224811Skarels if (m->m_len + oiplen > MLEN) 10324811Skarels oiplen = sizeof(struct ip); 10424811Skarels if (m->m_len + oiplen > MLEN) 10524811Skarels panic("icmp len"); 1066583Ssam m->m_off -= oiplen; 10727063Skarels m->m_len += oiplen; 1086583Ssam nip = mtod(m, struct ip *); 1096583Ssam bcopy((caddr_t)oip, (caddr_t)nip, oiplen); 11027063Skarels nip->ip_len = m->m_len; 1116583Ssam nip->ip_p = IPPROTO_ICMP; 11226034Skarels icmp_reflect(nip, ifp); 1134785Swnj 1146485Swnj free: 1154785Swnj m_freem(dtom(oip)); 1164785Swnj } 1174785Swnj 1186583Ssam static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP }; 1196583Ssam static struct sockaddr_in icmpsrc = { AF_INET }; 1206583Ssam static struct sockaddr_in icmpdst = { AF_INET }; 12124811Skarels static struct sockaddr_in icmpgw = { AF_INET }; 12224811Skarels struct in_ifaddr *ifptoia(); 1236583Ssam 1244785Swnj /* 1254801Swnj * Process a received ICMP message. 1264785Swnj */ 12724811Skarels icmp_input(m, ifp) 12827194Skarels register struct mbuf *m; 12924811Skarels struct ifnet *ifp; 1304785Swnj { 1314801Swnj register struct icmp *icp; 1324801Swnj register struct ip *ip = mtod(m, struct ip *); 1339185Ssam int icmplen = ip->ip_len, hlen = ip->ip_hl << 2; 13416171Skarels register int i; 13524811Skarels struct in_ifaddr *ia; 13616171Skarels int (*ctlfunc)(), code; 1375172Swnj extern u_char ip_protox[]; 13818373Skarels extern struct in_addr in_makeaddr(); 1394785Swnj 1404785Swnj /* 1414785Swnj * Locate icmp structure in mbuf, and check 1424785Swnj * that not corrupted and of at least minimum length. 1434785Swnj */ 14415027Smckusick #ifdef ICMPPRINTFS 1456590Ssam if (icmpprintfs) 1466590Ssam printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen); 14715027Smckusick #endif 14815913Skarels if (icmplen < ICMP_MINLEN) { 14911532Ssam icmpstat.icps_tooshort++; 1506583Ssam goto free; 15111532Ssam } 15224811Skarels i = hlen + MIN(icmplen, ICMP_ADVLENMIN); 15316171Skarels if ((m->m_off > MMAXOFF || m->m_len < i) && 15416171Skarels (m = m_pullup(m, i)) == 0) { 15515831Skarels icmpstat.icps_tooshort++; 15615831Skarels return; 15715831Skarels } 15816171Skarels ip = mtod(m, struct ip *); 1594785Swnj m->m_len -= hlen; 1604785Swnj m->m_off += hlen; 1616583Ssam icp = mtod(m, struct icmp *); 16216171Skarels if (in_cksum(m, icmplen)) { 16311532Ssam icmpstat.icps_checksum++; 1644801Swnj goto free; 1656583Ssam } 16627063Skarels m->m_len += hlen; 16727063Skarels m->m_off -= hlen; 1684785Swnj 16915027Smckusick #ifdef ICMPPRINTFS 1704785Swnj /* 1714785Swnj * Message type specific processing. 1724785Swnj */ 1736590Ssam if (icmpprintfs) 1746590Ssam printf("icmp_input, type %d code %d\n", icp->icmp_type, 17515027Smckusick icp->icmp_code); 17615027Smckusick #endif 17724811Skarels if (icp->icmp_type > ICMP_MAXTYPE) 17827194Skarels goto raw; 17911532Ssam icmpstat.icps_inhist[icp->icmp_type]++; 18015027Smckusick code = icp->icmp_code; 18111532Ssam switch (icp->icmp_type) { 1824785Swnj 1834785Swnj case ICMP_UNREACH: 18415027Smckusick if (code > 5) 18515027Smckusick goto badcode; 18615027Smckusick code += PRC_UNREACH_NET; 18715027Smckusick goto deliver; 18815027Smckusick 1894785Swnj case ICMP_TIMXCEED: 19015027Smckusick if (code > 1) 19115027Smckusick goto badcode; 19215027Smckusick code += PRC_TIMXCEED_INTRANS; 19315027Smckusick goto deliver; 19415027Smckusick 1954785Swnj case ICMP_PARAMPROB: 19615027Smckusick if (code) 19715027Smckusick goto badcode; 19815027Smckusick code = PRC_PARAMPROB; 19915027Smckusick goto deliver; 20015027Smckusick 2014785Swnj case ICMP_SOURCEQUENCH: 20215027Smckusick if (code) 20315027Smckusick goto badcode; 20415027Smckusick code = PRC_QUENCH; 20515027Smckusick deliver: 2064785Swnj /* 20715027Smckusick * Problem with datagram; advise higher level routines. 2084785Swnj */ 2098637Sroot icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len); 21011532Ssam if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { 21111532Ssam icmpstat.icps_badlen++; 2124801Swnj goto free; 21311532Ssam } 21415027Smckusick #ifdef ICMPPRINTFS 2156590Ssam if (icmpprintfs) 2166590Ssam printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); 21715027Smckusick #endif 21824811Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 2199030Sroot if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput) 22024811Skarels (*ctlfunc)(code, (struct sockaddr *)&icmpsrc); 22127063Skarels break; 2224785Swnj 22315027Smckusick badcode: 22415027Smckusick icmpstat.icps_badcode++; 22527063Skarels break; 22615027Smckusick 2274785Swnj case ICMP_ECHO: 2284785Swnj icp->icmp_type = ICMP_ECHOREPLY; 2294785Swnj goto reflect; 2304785Swnj 2314785Swnj case ICMP_TSTAMP: 23211532Ssam if (icmplen < ICMP_TSLEN) { 23311532Ssam icmpstat.icps_badlen++; 23427063Skarels break; 23511532Ssam } 2364785Swnj icp->icmp_type = ICMP_TSTAMPREPLY; 2374923Swnj icp->icmp_rtime = iptime(); 2384785Swnj icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */ 2394785Swnj goto reflect; 2404785Swnj 2414785Swnj case ICMP_IREQ: 24218373Skarels #define satosin(sa) ((struct sockaddr_in *)(sa)) 24324811Skarels if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp))) 24424811Skarels ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr), 24518373Skarels in_lnaof(ip->ip_src)); 24618373Skarels icp->icmp_type = ICMP_IREQREPLY; 2474785Swnj goto reflect; 2484785Swnj 24924811Skarels case ICMP_MASKREQ: 25024811Skarels if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0) 25127063Skarels break; 25224811Skarels icp->icmp_type = ICMP_IREQREPLY; 25324811Skarels icp->icmp_mask = ia->ia_netmask; 25424811Skarels if (ip->ip_src.s_addr == 0) { 25524811Skarels if (ia->ia_ifp->if_flags & IFF_BROADCAST) 25624811Skarels ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; 25724811Skarels else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) 25824811Skarels ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; 25924811Skarels } 26027063Skarels reflect: 26127063Skarels ip->ip_len += hlen; /* since ip_input deducts this */ 26227063Skarels icmpstat.icps_reflect++; 26327063Skarels icmpstat.icps_outhist[icp->icmp_type]++; 26427063Skarels icmp_reflect(ip, ifp); 26527063Skarels return; 26624811Skarels 26711549Ssam case ICMP_REDIRECT: 26811532Ssam if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { 26911532Ssam icmpstat.icps_badlen++; 27027063Skarels break; 27111532Ssam } 27211549Ssam /* 27311549Ssam * Short circuit routing redirects to force 27411549Ssam * immediate change in the kernel's routing 27511549Ssam * tables. The message is also handed to anyone 27611549Ssam * listening on a raw socket (e.g. the routing 27717046Skarels * daemon for use in updating its tables). 27811549Ssam */ 27924811Skarels icmpgw.sin_addr = ip->ip_src; 28015831Skarels icmpdst.sin_addr = icp->icmp_gwaddr; 28124811Skarels #ifdef ICMPPRINTFS 28224811Skarels if (icmpprintfs) 28324811Skarels printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst, 28424811Skarels icp->icmp_gwaddr); 28524811Skarels #endif 28617046Skarels if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) { 28717046Skarels icmpsrc.sin_addr = 28818373Skarels in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY); 28917046Skarels rtredirect((struct sockaddr *)&icmpsrc, 29024811Skarels (struct sockaddr *)&icmpdst, RTF_GATEWAY, 29124811Skarels (struct sockaddr *)&icmpgw); 29227194Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 29324811Skarels pfctlinput(PRC_REDIRECT_NET, 29424811Skarels (struct sockaddr *)&icmpsrc); 29517046Skarels } else { 29617046Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 29717046Skarels rtredirect((struct sockaddr *)&icmpsrc, 29824811Skarels (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST, 29924811Skarels (struct sockaddr *)&icmpgw); 30024811Skarels pfctlinput(PRC_REDIRECT_HOST, 30124811Skarels (struct sockaddr *)&icmpsrc); 30217046Skarels } 30327063Skarels break; 30415831Skarels 30527063Skarels /* 30627063Skarels * No kernel processing for the following; 30727063Skarels * just fall through to send to raw listener. 30827063Skarels */ 30915831Skarels case ICMP_ECHOREPLY: 31015831Skarels case ICMP_TSTAMPREPLY: 31115831Skarels case ICMP_IREQREPLY: 31224811Skarels case ICMP_MASKREPLY: 3134785Swnj default: 31427063Skarels break; 3154785Swnj } 31627063Skarels 31727194Skarels raw: 31827063Skarels icmpsrc.sin_addr = ip->ip_src; 31927063Skarels icmpdst.sin_addr = ip->ip_dst; 32027063Skarels raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc, 32127063Skarels (struct sockaddr *)&icmpdst); 3229185Ssam return; 32327063Skarels 3244785Swnj free: 32527063Skarels m_freem(m); 3264785Swnj } 3274785Swnj 3284785Swnj /* 3294785Swnj * Reflect the ip packet back to the source 3304785Swnj */ 33124811Skarels icmp_reflect(ip, ifp) 33226034Skarels register struct ip *ip; 33324811Skarels struct ifnet *ifp; 3344785Swnj { 33518373Skarels register struct in_ifaddr *ia; 33624811Skarels struct in_addr t; 337*28926Skarels struct mbuf *opts = 0, *ip_srcroute(); 33827063Skarels int optlen = (ip->ip_hl << 2) - sizeof(struct ip); 3394785Swnj 3406583Ssam t = ip->ip_dst; 34124811Skarels ip->ip_dst = ip->ip_src; 34224811Skarels /* 34324811Skarels * If the incoming packet was addressed directly to us, 34424811Skarels * use dst as the src for the reply. Otherwise (broadcast 34524811Skarels * or anonymous), use the address which corresponds 34624811Skarels * to the incoming interface. 34724811Skarels */ 34824811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) { 34924811Skarels if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) 35018373Skarels break; 35124811Skarels if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && 35224811Skarels t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr) 35324811Skarels break; 35424811Skarels } 35524811Skarels if (ia == (struct in_ifaddr *)0) 35624811Skarels ia = ifptoia(ifp); 35728344Skarels if (ia == (struct in_ifaddr *)0) 35828344Skarels ia = in_ifaddr; 35928344Skarels t = IA_SIN(ia)->sin_addr; 36024811Skarels ip->ip_src = t; 36124811Skarels 36227063Skarels if (optlen > 0) { 36326034Skarels /* 36426034Skarels * Retrieve any source routing from the incoming packet 36527063Skarels * and strip out other options. Adjust the IP length. 36626034Skarels */ 36726034Skarels opts = ip_srcroute(); 36826034Skarels ip->ip_len -= optlen; 36926034Skarels ip_stripoptions(ip, (struct mbuf *)0); 37024811Skarels } 37126034Skarels icmp_send(ip, opts); 37226034Skarels if (opts) 37326383Skarels (void)m_free(opts); 3744785Swnj } 3754785Swnj 37624811Skarels struct in_ifaddr * 37724811Skarels ifptoia(ifp) 37824811Skarels struct ifnet *ifp; 37924811Skarels { 38024811Skarels register struct in_ifaddr *ia; 38124811Skarels 38224811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 38324811Skarels if (ia->ia_ifp == ifp) 38424811Skarels return (ia); 38524811Skarels return ((struct in_ifaddr *)0); 38624811Skarels } 38724811Skarels 3884785Swnj /* 3896583Ssam * Send an icmp packet back to the ip level, 3906583Ssam * after supplying a checksum. 3914785Swnj */ 39226034Skarels icmp_send(ip, opts) 39326034Skarels register struct ip *ip; 39426034Skarels struct mbuf *opts; 3954785Swnj { 3969185Ssam register int hlen; 3976583Ssam register struct icmp *icp; 3989185Ssam register struct mbuf *m; 3996583Ssam 4009185Ssam m = dtom(ip); 4019185Ssam hlen = ip->ip_hl << 2; 40227063Skarels m->m_off += hlen; 40327063Skarels m->m_len -= hlen; 4046583Ssam icp = mtod(m, struct icmp *); 4056583Ssam icp->icmp_cksum = 0; 4066583Ssam icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); 4076583Ssam m->m_off -= hlen; 4086583Ssam m->m_len += hlen; 40915027Smckusick #ifdef ICMPPRINTFS 4106590Ssam if (icmpprintfs) 4116590Ssam printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src); 41215027Smckusick #endif 41326034Skarels (void) ip_output(m, opts, (struct route *)0, 0); 4144785Swnj } 4154785Swnj 4164907Swnj n_time 4174923Swnj iptime() 4184801Swnj { 41925893Skarels struct timeval atv; 4204967Swnj u_long t; 4214801Swnj 42225893Skarels microtime(&atv); 42325893Skarels t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000; 4244907Swnj return (htonl(t)); 4254801Swnj } 426