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*26383Skarels * @(#)ip_icmp.c 6.16 (Berkeley) 02/23/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; 51*26383Skarels 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 /* 1006583Ssam * Now, copy old ip header in front of icmp 1016583Ssam * message. This allows us to reuse any source 1026583Ssam * routing info present. 1034785Swnj */ 10424811Skarels if (m->m_len + oiplen > MLEN) 10524811Skarels oiplen = sizeof(struct ip); 10624811Skarels if (m->m_len + oiplen > MLEN) 10724811Skarels panic("icmp len"); 1086583Ssam m->m_off -= oiplen; 1096583Ssam nip = mtod(m, struct ip *); 1106583Ssam bcopy((caddr_t)oip, (caddr_t)nip, oiplen); 1116583Ssam nip->ip_len = m->m_len + oiplen; 1126583Ssam nip->ip_p = IPPROTO_ICMP; 1136583Ssam /* icmp_send adds ip header to m_off and m_len, so we deduct here */ 1146583Ssam m->m_off += oiplen; 11526034Skarels icmp_reflect(nip, ifp); 1164785Swnj 1176485Swnj free: 1184785Swnj m_freem(dtom(oip)); 1194785Swnj } 1204785Swnj 1216583Ssam static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP }; 1226583Ssam static struct sockaddr_in icmpsrc = { AF_INET }; 1236583Ssam static struct sockaddr_in icmpdst = { AF_INET }; 12424811Skarels static struct sockaddr_in icmpgw = { AF_INET }; 12524811Skarels struct in_ifaddr *ifptoia(); 1266583Ssam 1274785Swnj /* 1284801Swnj * Process a received ICMP message. 1294785Swnj */ 13024811Skarels icmp_input(m, ifp) 1314785Swnj struct mbuf *m; 13224811Skarels struct ifnet *ifp; 1334785Swnj { 1344801Swnj register struct icmp *icp; 1354801Swnj register struct ip *ip = mtod(m, struct ip *); 1369185Ssam int icmplen = ip->ip_len, hlen = ip->ip_hl << 2; 13716171Skarels register int i; 13824811Skarels struct in_ifaddr *ia; 13916171Skarels int (*ctlfunc)(), code; 1405172Swnj extern u_char ip_protox[]; 14118373Skarels extern struct in_addr in_makeaddr(); 1424785Swnj 1434785Swnj /* 1444785Swnj * Locate icmp structure in mbuf, and check 1454785Swnj * that not corrupted and of at least minimum length. 1464785Swnj */ 14715027Smckusick #ifdef ICMPPRINTFS 1486590Ssam if (icmpprintfs) 1496590Ssam printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen); 15015027Smckusick #endif 15115913Skarels if (icmplen < ICMP_MINLEN) { 15211532Ssam icmpstat.icps_tooshort++; 1536583Ssam goto free; 15411532Ssam } 15524811Skarels i = hlen + MIN(icmplen, ICMP_ADVLENMIN); 15616171Skarels if ((m->m_off > MMAXOFF || m->m_len < i) && 15716171Skarels (m = m_pullup(m, i)) == 0) { 15815831Skarels icmpstat.icps_tooshort++; 15915831Skarels return; 16015831Skarels } 16116171Skarels ip = mtod(m, struct ip *); 1624785Swnj m->m_len -= hlen; 1634785Swnj m->m_off += hlen; 1646583Ssam icp = mtod(m, struct icmp *); 16516171Skarels if (in_cksum(m, icmplen)) { 16611532Ssam icmpstat.icps_checksum++; 1674801Swnj goto free; 1686583Ssam } 1694785Swnj 17015027Smckusick #ifdef ICMPPRINTFS 1714785Swnj /* 1724785Swnj * Message type specific processing. 1734785Swnj */ 1746590Ssam if (icmpprintfs) 1756590Ssam printf("icmp_input, type %d code %d\n", icp->icmp_type, 17615027Smckusick icp->icmp_code); 17715027Smckusick #endif 17824811Skarels if (icp->icmp_type > ICMP_MAXTYPE) 17911532Ssam goto free; 18011532Ssam icmpstat.icps_inhist[icp->icmp_type]++; 18115027Smckusick code = icp->icmp_code; 18211532Ssam switch (icp->icmp_type) { 1834785Swnj 1844785Swnj case ICMP_UNREACH: 18515027Smckusick if (code > 5) 18615027Smckusick goto badcode; 18715027Smckusick code += PRC_UNREACH_NET; 18815027Smckusick goto deliver; 18915027Smckusick 1904785Swnj case ICMP_TIMXCEED: 19115027Smckusick if (code > 1) 19215027Smckusick goto badcode; 19315027Smckusick code += PRC_TIMXCEED_INTRANS; 19415027Smckusick goto deliver; 19515027Smckusick 1964785Swnj case ICMP_PARAMPROB: 19715027Smckusick if (code) 19815027Smckusick goto badcode; 19915027Smckusick code = PRC_PARAMPROB; 20015027Smckusick goto deliver; 20115027Smckusick 2024785Swnj case ICMP_SOURCEQUENCH: 20315027Smckusick if (code) 20415027Smckusick goto badcode; 20515027Smckusick code = PRC_QUENCH; 20615027Smckusick deliver: 2074785Swnj /* 20815027Smckusick * Problem with datagram; advise higher level routines. 2094785Swnj */ 2108637Sroot icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len); 21111532Ssam if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { 21211532Ssam icmpstat.icps_badlen++; 2134801Swnj goto free; 21411532Ssam } 21515027Smckusick #ifdef ICMPPRINTFS 2166590Ssam if (icmpprintfs) 2176590Ssam printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); 21815027Smckusick #endif 21924811Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 2209030Sroot if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput) 22124811Skarels (*ctlfunc)(code, (struct sockaddr *)&icmpsrc); 2224785Swnj goto free; 2234785Swnj 22415027Smckusick badcode: 22515027Smckusick icmpstat.icps_badcode++; 22615027Smckusick goto free; 22715027Smckusick 2284785Swnj case ICMP_ECHO: 2294785Swnj icp->icmp_type = ICMP_ECHOREPLY; 2304785Swnj goto reflect; 2314785Swnj 2324785Swnj case ICMP_TSTAMP: 23311532Ssam if (icmplen < ICMP_TSLEN) { 23411532Ssam icmpstat.icps_badlen++; 2354801Swnj goto free; 23611532Ssam } 2374785Swnj icp->icmp_type = ICMP_TSTAMPREPLY; 2384923Swnj icp->icmp_rtime = iptime(); 2394785Swnj icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */ 2404785Swnj goto reflect; 2414785Swnj 2424785Swnj case ICMP_IREQ: 24318373Skarels #define satosin(sa) ((struct sockaddr_in *)(sa)) 24424811Skarels if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp))) 24524811Skarels ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr), 24618373Skarels in_lnaof(ip->ip_src)); 24718373Skarels icp->icmp_type = ICMP_IREQREPLY; 2484785Swnj goto reflect; 2494785Swnj 25024811Skarels case ICMP_MASKREQ: 25124811Skarels if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0) 25224811Skarels goto free; 25324811Skarels icp->icmp_type = ICMP_IREQREPLY; 25424811Skarels icp->icmp_mask = ia->ia_netmask; 25524811Skarels if (ip->ip_src.s_addr == 0) { 25624811Skarels if (ia->ia_ifp->if_flags & IFF_BROADCAST) 25724811Skarels ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; 25824811Skarels else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) 25924811Skarels ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; 26024811Skarels } 26124811Skarels goto reflect; 26224811Skarels 26311549Ssam case ICMP_REDIRECT: 26411532Ssam if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { 26511532Ssam icmpstat.icps_badlen++; 2664801Swnj goto free; 26711532Ssam } 26811549Ssam /* 26911549Ssam * Short circuit routing redirects to force 27011549Ssam * immediate change in the kernel's routing 27111549Ssam * tables. The message is also handed to anyone 27211549Ssam * listening on a raw socket (e.g. the routing 27317046Skarels * daemon for use in updating its tables). 27411549Ssam */ 27524811Skarels icmpgw.sin_addr = ip->ip_src; 27615831Skarels icmpdst.sin_addr = icp->icmp_gwaddr; 27724811Skarels #ifdef ICMPPRINTFS 27824811Skarels if (icmpprintfs) 27924811Skarels printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst, 28024811Skarels icp->icmp_gwaddr); 28124811Skarels #endif 28217046Skarels if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) { 28317046Skarels icmpsrc.sin_addr = 28418373Skarels in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY); 28517046Skarels rtredirect((struct sockaddr *)&icmpsrc, 28624811Skarels (struct sockaddr *)&icmpdst, RTF_GATEWAY, 28724811Skarels (struct sockaddr *)&icmpgw); 28824811Skarels pfctlinput(PRC_REDIRECT_NET, 28924811Skarels (struct sockaddr *)&icmpsrc); 29017046Skarels } else { 29117046Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 29217046Skarels rtredirect((struct sockaddr *)&icmpsrc, 29324811Skarels (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST, 29424811Skarels (struct sockaddr *)&icmpgw); 29524811Skarels pfctlinput(PRC_REDIRECT_HOST, 29624811Skarels (struct sockaddr *)&icmpsrc); 29717046Skarels } 29815831Skarels /* FALL THROUGH */ 29915831Skarels 30015831Skarels case ICMP_ECHOREPLY: 30115831Skarels case ICMP_TSTAMPREPLY: 30215831Skarels case ICMP_IREQREPLY: 30324811Skarels case ICMP_MASKREPLY: 3046583Ssam icmpsrc.sin_addr = ip->ip_src; 3056583Ssam icmpdst.sin_addr = ip->ip_dst; 3066583Ssam raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc, 3076583Ssam (struct sockaddr *)&icmpdst); 30812165Ssam return; 3094785Swnj 3104785Swnj default: 3114785Swnj goto free; 3124785Swnj } 3134785Swnj reflect: 31411228Ssam ip->ip_len += hlen; /* since ip_input deducts this */ 31511532Ssam icmpstat.icps_reflect++; 31618643Skarels icmpstat.icps_outhist[icp->icmp_type]++; 31724811Skarels icmp_reflect(ip, ifp); 3189185Ssam return; 3194785Swnj free: 3204785Swnj m_freem(dtom(ip)); 3214785Swnj } 3224785Swnj 3234785Swnj /* 3244785Swnj * Reflect the ip packet back to the source 3254785Swnj */ 32624811Skarels icmp_reflect(ip, ifp) 32726034Skarels register struct ip *ip; 32824811Skarels struct ifnet *ifp; 3294785Swnj { 33018373Skarels register struct in_ifaddr *ia; 33124811Skarels struct in_addr t; 33226034Skarels struct mbuf *m, *opts = 0, *ip_srcroute(); 3334785Swnj 3346583Ssam t = ip->ip_dst; 33524811Skarels ip->ip_dst = ip->ip_src; 33624811Skarels /* 33724811Skarels * If the incoming packet was addressed directly to us, 33824811Skarels * use dst as the src for the reply. Otherwise (broadcast 33924811Skarels * or anonymous), use the address which corresponds 34024811Skarels * to the incoming interface. 34124811Skarels */ 34224811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) { 34324811Skarels if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) 34418373Skarels break; 34524811Skarels if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && 34624811Skarels t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr) 34724811Skarels break; 34824811Skarels } 34924811Skarels if (ia == (struct in_ifaddr *)0) 35024811Skarels ia = ifptoia(ifp); 35124811Skarels if (ia) 35224811Skarels t = IA_SIN(ia)->sin_addr; 35324811Skarels ip->ip_src = t; 35424811Skarels 35524811Skarels if ((ip->ip_hl << 2) > sizeof(struct ip)) { 35626034Skarels int optlen = (ip->ip_hl << 2) - sizeof(struct ip); 35726034Skarels 35826034Skarels /* 35926034Skarels * Retrieve any source routing from the incoming packet 36026034Skarels * and strip out other options. Adjust the IP 36126034Skarels * and mbuf lengths. The length of the options is added 36226034Skarels * to the mbuf here, as it was already subtracted 36326034Skarels * in icmp_input, and ip_stripoptions will subtract it again. 36426034Skarels * Adjust the mbuf offset to point to the new location 36526034Skarels * of the icmp header. 36626034Skarels */ 36726034Skarels opts = ip_srcroute(); 36826034Skarels m = dtom(ip); 36926034Skarels m->m_off -= optlen; 37026034Skarels m->m_len += optlen; 37126034Skarels ip->ip_len -= optlen; 37226034Skarels ip_stripoptions(ip, (struct mbuf *)0); 37324811Skarels } 37426034Skarels icmp_send(ip, opts); 37526034Skarels if (opts) 376*26383Skarels (void)m_free(opts); 3774785Swnj } 3784785Swnj 37924811Skarels struct in_ifaddr * 38024811Skarels ifptoia(ifp) 38124811Skarels struct ifnet *ifp; 38224811Skarels { 38324811Skarels register struct in_ifaddr *ia; 38424811Skarels 38524811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 38624811Skarels if (ia->ia_ifp == ifp) 38724811Skarels return (ia); 38824811Skarels return ((struct in_ifaddr *)0); 38924811Skarels } 39024811Skarels 3914785Swnj /* 3926583Ssam * Send an icmp packet back to the ip level, 3936583Ssam * after supplying a checksum. 3944785Swnj */ 39526034Skarels icmp_send(ip, opts) 39626034Skarels register struct ip *ip; 39726034Skarels struct mbuf *opts; 3984785Swnj { 3999185Ssam register int hlen; 4006583Ssam register struct icmp *icp; 4019185Ssam register struct mbuf *m; 4026583Ssam 4039185Ssam m = dtom(ip); 4049185Ssam hlen = ip->ip_hl << 2; 4056583Ssam icp = mtod(m, struct icmp *); 4066583Ssam icp->icmp_cksum = 0; 4076583Ssam icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); 4086583Ssam m->m_off -= hlen; 4096583Ssam m->m_len += hlen; 41015027Smckusick #ifdef ICMPPRINTFS 4116590Ssam if (icmpprintfs) 4126590Ssam printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src); 41315027Smckusick #endif 41426034Skarels (void) ip_output(m, opts, (struct route *)0, 0); 4154785Swnj } 4164785Swnj 4174907Swnj n_time 4184923Swnj iptime() 4194801Swnj { 42025893Skarels struct timeval atv; 4214967Swnj u_long t; 4224801Swnj 42325893Skarels microtime(&atv); 42425893Skarels t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000; 4254907Swnj return (htonl(t)); 4264801Swnj } 427