123182Smckusick /* 2*54716Ssklower * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 332787Sbostic * All rights reserved. 423182Smckusick * 544478Sbostic * %sccs.include.redist.c% 632787Sbostic * 7*54716Ssklower * @(#)ip_icmp.c 7.16 (Berkeley) 07/06/92 823182Smckusick */ 94785Swnj 1017059Sbloom #include "param.h" 1117059Sbloom #include "systm.h" 1240684Skarels #include "malloc.h" 1317059Sbloom #include "mbuf.h" 1417059Sbloom #include "protosw.h" 1517059Sbloom #include "socket.h" 1617059Sbloom #include "time.h" 1717059Sbloom #include "kernel.h" 188694Sroot 198694Sroot #include "../net/route.h" 2018373Skarels #include "../net/if.h" 2110891Ssam 2217059Sbloom #include "in.h" 2317059Sbloom #include "in_systm.h" 2418373Skarels #include "in_var.h" 2517059Sbloom #include "ip.h" 2617059Sbloom #include "ip_icmp.h" 2717059Sbloom #include "icmp_var.h" 284785Swnj 294785Swnj /* 304785Swnj * ICMP routines: error generation, receive packet processing, and 314785Swnj * routines to turnaround packets back to the originator, and 324785Swnj * host table maintenance routines. 334785Swnj */ 3440684Skarels #ifdef ICMPPRINTFS 356608Ssam int icmpprintfs = 0; 3615027Smckusick #endif 374785Swnj 3848463Skarels extern struct protosw inetsw[]; 3948463Skarels 404785Swnj /* 416583Ssam * Generate an error packet of type error 426583Ssam * in response to bad packet ip. 434785Swnj */ 4440684Skarels /*VARARGS3*/ 4540684Skarels icmp_error(n, type, code, dest) 4640684Skarels struct mbuf *n; 4712764Ssam int type, code; 4824811Skarels struct in_addr dest; 494785Swnj { 5040684Skarels register struct ip *oip = mtod(n, struct ip *), *nip; 516583Ssam register unsigned oiplen = oip->ip_hl << 2; 526583Ssam register struct icmp *icp; 5335794Skarels register struct mbuf *m; 5426383Skarels unsigned icmplen; 554785Swnj 5615027Smckusick #ifdef ICMPPRINTFS 576590Ssam if (icmpprintfs) 586590Ssam printf("icmp_error(%x, %d, %d)\n", oip, type, code); 5915027Smckusick #endif 6026034Skarels if (type != ICMP_REDIRECT) 6126034Skarels icmpstat.icps_error++; 624785Swnj /* 6324811Skarels * Don't send error if not the first fragment of message. 6431399Skarels * Don't error if the old packet protocol was ICMP 6531399Skarels * error message, only known informational types. 664785Swnj */ 6724811Skarels if (oip->ip_off &~ (IP_MF|IP_DF)) 6840684Skarels goto freeit; 6931399Skarels if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT && 7040684Skarels n->m_len >= oiplen + ICMP_MINLEN && 7131399Skarels !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) { 7211532Ssam icmpstat.icps_oldicmp++; 7340684Skarels goto freeit; 7411532Ssam } 75*54716Ssklower #ifdef MULTICAST 76*54716Ssklower /* Don't send error in response to a multicast or broadcast packet */ 77*54716Ssklower if (n->m_flags & (M_MCAST | M_BCAST)) 78*54716Ssklower goto freeit; 79*54716Ssklower #endif 804785Swnj /* 816583Ssam * First, formulate icmp message 824785Swnj */ 8340684Skarels m = m_gethdr(M_DONTWAIT, MT_HEADER); 8411532Ssam if (m == NULL) 8540684Skarels goto freeit; 8635794Skarels icmplen = oiplen + min(8, oip->ip_len); 8724811Skarels m->m_len = icmplen + ICMP_MINLEN; 8840684Skarels MH_ALIGN(m, m->m_len); 896583Ssam icp = mtod(m, struct icmp *); 9024811Skarels if ((u_int)type > ICMP_MAXTYPE) 9111532Ssam panic("icmp_error"); 9211532Ssam icmpstat.icps_outhist[type]++; 934785Swnj icp->icmp_type = type; 9424811Skarels if (type == ICMP_REDIRECT) 9524811Skarels icp->icmp_gwaddr = dest; 9624811Skarels else 9724811Skarels icp->icmp_void = 0; 984785Swnj if (type == ICMP_PARAMPROB) { 994785Swnj icp->icmp_pptr = code; 1006583Ssam code = 0; 1016583Ssam } 1026583Ssam icp->icmp_code = code; 10324811Skarels bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen); 1046590Ssam nip = &icp->icmp_ip; 10535794Skarels nip->ip_len = htons((u_short)(nip->ip_len + oiplen)); 1064785Swnj 1074785Swnj /* 10836817Skarels * Now, copy old ip header (without options) 10936817Skarels * in front of icmp message. 1104785Swnj */ 11140684Skarels if (m->m_data - sizeof(struct ip) < m->m_pktdat) 11224811Skarels panic("icmp len"); 11340684Skarels m->m_data -= sizeof(struct ip); 11436817Skarels m->m_len += sizeof(struct ip); 11540684Skarels m->m_pkthdr.len = m->m_len; 11640684Skarels m->m_pkthdr.rcvif = n->m_pkthdr.rcvif; 1176583Ssam nip = mtod(m, struct ip *); 1186583Ssam bcopy((caddr_t)oip, (caddr_t)nip, oiplen); 11927063Skarels nip->ip_len = m->m_len; 12036817Skarels nip->ip_hl = sizeof(struct ip) >> 2; 1216583Ssam nip->ip_p = IPPROTO_ICMP; 12240684Skarels icmp_reflect(m); 1234785Swnj 12440684Skarels freeit: 12540684Skarels m_freem(n); 1264785Swnj } 1274785Swnj 1286583Ssam static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP }; 12940684Skarels static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET }; 13040684Skarels static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET }; 13140684Skarels static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET }; 13240684Skarels struct sockaddr_in icmpmask = { 8, 0 }; 13324811Skarels struct in_ifaddr *ifptoia(); 1346583Ssam 1354785Swnj /* 1364801Swnj * Process a received ICMP message. 1374785Swnj */ 13840684Skarels icmp_input(m, hlen) 13927194Skarels register struct mbuf *m; 14040684Skarels int hlen; 1414785Swnj { 1424801Swnj register struct icmp *icp; 1434801Swnj register struct ip *ip = mtod(m, struct ip *); 14440684Skarels int icmplen = ip->ip_len; 14516171Skarels register int i; 14624811Skarels struct in_ifaddr *ia; 14716171Skarels int (*ctlfunc)(), code; 1485172Swnj extern u_char ip_protox[]; 14918373Skarels extern struct in_addr in_makeaddr(); 1504785Swnj 1514785Swnj /* 1524785Swnj * Locate icmp structure in mbuf, and check 1534785Swnj * that not corrupted and of at least minimum length. 1544785Swnj */ 15515027Smckusick #ifdef ICMPPRINTFS 1566590Ssam if (icmpprintfs) 1576590Ssam printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen); 15815027Smckusick #endif 15915913Skarels if (icmplen < ICMP_MINLEN) { 16011532Ssam icmpstat.icps_tooshort++; 16140684Skarels goto freeit; 16211532Ssam } 16324811Skarels i = hlen + MIN(icmplen, ICMP_ADVLENMIN); 164*54716Ssklower if (m->m_len < i && (m = m_pullup(m, i)) == 0) { 16515831Skarels icmpstat.icps_tooshort++; 16615831Skarels return; 16715831Skarels } 168*54716Ssklower ip = mtod(m, struct ip *); 1694785Swnj m->m_len -= hlen; 17040684Skarels m->m_data += hlen; 1716583Ssam icp = mtod(m, struct icmp *); 17216171Skarels if (in_cksum(m, icmplen)) { 17311532Ssam icmpstat.icps_checksum++; 17440684Skarels goto freeit; 1756583Ssam } 17627063Skarels m->m_len += hlen; 17740684Skarels m->m_data -= hlen; 1784785Swnj 17915027Smckusick #ifdef ICMPPRINTFS 1804785Swnj /* 1814785Swnj * Message type specific processing. 1824785Swnj */ 1836590Ssam if (icmpprintfs) 1846590Ssam printf("icmp_input, type %d code %d\n", icp->icmp_type, 18515027Smckusick icp->icmp_code); 18615027Smckusick #endif 18724811Skarels if (icp->icmp_type > ICMP_MAXTYPE) 18827194Skarels goto raw; 18911532Ssam icmpstat.icps_inhist[icp->icmp_type]++; 19015027Smckusick code = icp->icmp_code; 19111532Ssam switch (icp->icmp_type) { 1924785Swnj 1934785Swnj case ICMP_UNREACH: 19415027Smckusick if (code > 5) 19515027Smckusick goto badcode; 19615027Smckusick code += PRC_UNREACH_NET; 19715027Smckusick goto deliver; 19815027Smckusick 1994785Swnj case ICMP_TIMXCEED: 20015027Smckusick if (code > 1) 20115027Smckusick goto badcode; 20215027Smckusick code += PRC_TIMXCEED_INTRANS; 20315027Smckusick goto deliver; 20415027Smckusick 2054785Swnj case ICMP_PARAMPROB: 20615027Smckusick if (code) 20715027Smckusick goto badcode; 20815027Smckusick code = PRC_PARAMPROB; 20915027Smckusick goto deliver; 21015027Smckusick 2114785Swnj case ICMP_SOURCEQUENCH: 21215027Smckusick if (code) 21315027Smckusick goto badcode; 21415027Smckusick code = PRC_QUENCH; 21515027Smckusick deliver: 2164785Swnj /* 21715027Smckusick * Problem with datagram; advise higher level routines. 2184785Swnj */ 21940684Skarels if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) || 22040684Skarels icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) { 22111532Ssam icmpstat.icps_badlen++; 22240684Skarels goto freeit; 22311532Ssam } 22440684Skarels NTOHS(icp->icmp_ip.ip_len); 22515027Smckusick #ifdef ICMPPRINTFS 2266590Ssam if (icmpprintfs) 2276590Ssam printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); 22815027Smckusick #endif 22924811Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 2309030Sroot if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput) 23140684Skarels (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, 23240684Skarels (caddr_t) &icp->icmp_ip); 23327063Skarels break; 2344785Swnj 23515027Smckusick badcode: 23615027Smckusick icmpstat.icps_badcode++; 23727063Skarels break; 23815027Smckusick 2394785Swnj case ICMP_ECHO: 2404785Swnj icp->icmp_type = ICMP_ECHOREPLY; 2414785Swnj goto reflect; 2424785Swnj 2434785Swnj case ICMP_TSTAMP: 24411532Ssam if (icmplen < ICMP_TSLEN) { 24511532Ssam icmpstat.icps_badlen++; 24627063Skarels break; 24711532Ssam } 2484785Swnj icp->icmp_type = ICMP_TSTAMPREPLY; 2494923Swnj icp->icmp_rtime = iptime(); 2504785Swnj icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */ 2514785Swnj goto reflect; 2524785Swnj 2534785Swnj case ICMP_IREQ: 25418373Skarels #define satosin(sa) ((struct sockaddr_in *)(sa)) 25540684Skarels if (in_netof(ip->ip_src) == 0 && 25640684Skarels (ia = ifptoia(m->m_pkthdr.rcvif))) 25724811Skarels ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr), 25818373Skarels in_lnaof(ip->ip_src)); 25918373Skarels icp->icmp_type = ICMP_IREQREPLY; 2604785Swnj goto reflect; 2614785Swnj 26224811Skarels case ICMP_MASKREQ: 26340684Skarels if (icmplen < ICMP_MASKLEN || 264*54716Ssklower m->m_flags & M_BCAST || /* Don't reply to broadcasts */ 26540684Skarels (ia = ifptoia(m->m_pkthdr.rcvif)) == 0) 26627063Skarels break; 26730362Skarels icp->icmp_type = ICMP_MASKREPLY; 26840684Skarels icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr; 26924811Skarels if (ip->ip_src.s_addr == 0) { 27024811Skarels if (ia->ia_ifp->if_flags & IFF_BROADCAST) 27124811Skarels ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; 27224811Skarels else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) 27324811Skarels ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; 27424811Skarels } 27527063Skarels reflect: 27627063Skarels ip->ip_len += hlen; /* since ip_input deducts this */ 27727063Skarels icmpstat.icps_reflect++; 27827063Skarels icmpstat.icps_outhist[icp->icmp_type]++; 27940684Skarels icmp_reflect(m); 28027063Skarels return; 28124811Skarels 28211549Ssam case ICMP_REDIRECT: 28311532Ssam if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { 28411532Ssam icmpstat.icps_badlen++; 28527063Skarels break; 28611532Ssam } 28711549Ssam /* 28811549Ssam * Short circuit routing redirects to force 28911549Ssam * immediate change in the kernel's routing 29011549Ssam * tables. The message is also handed to anyone 29111549Ssam * listening on a raw socket (e.g. the routing 29217046Skarels * daemon for use in updating its tables). 29311549Ssam */ 29424811Skarels icmpgw.sin_addr = ip->ip_src; 29515831Skarels icmpdst.sin_addr = icp->icmp_gwaddr; 29624811Skarels #ifdef ICMPPRINTFS 29724811Skarels if (icmpprintfs) 29824811Skarels printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst, 29924811Skarels icp->icmp_gwaddr); 30024811Skarels #endif 30117046Skarels if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) { 30240684Skarels u_long in_netof(); 30317046Skarels icmpsrc.sin_addr = 30418373Skarels in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY); 30540684Skarels in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask); 30617046Skarels rtredirect((struct sockaddr *)&icmpsrc, 30740684Skarels (struct sockaddr *)&icmpdst, 30840684Skarels (struct sockaddr *)&icmpmask, RTF_GATEWAY, 30940684Skarels (struct sockaddr *)&icmpgw, (struct rtentry **)0); 31027194Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 31124811Skarels pfctlinput(PRC_REDIRECT_NET, 31224811Skarels (struct sockaddr *)&icmpsrc); 31317046Skarels } else { 31417046Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 31517046Skarels rtredirect((struct sockaddr *)&icmpsrc, 31640684Skarels (struct sockaddr *)&icmpdst, 31740684Skarels (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST, 31840684Skarels (struct sockaddr *)&icmpgw, (struct rtentry **)0); 31924811Skarels pfctlinput(PRC_REDIRECT_HOST, 32024811Skarels (struct sockaddr *)&icmpsrc); 32117046Skarels } 32227063Skarels break; 32315831Skarels 32427063Skarels /* 32527063Skarels * No kernel processing for the following; 32627063Skarels * just fall through to send to raw listener. 32727063Skarels */ 32815831Skarels case ICMP_ECHOREPLY: 32915831Skarels case ICMP_TSTAMPREPLY: 33015831Skarels case ICMP_IREQREPLY: 33124811Skarels case ICMP_MASKREPLY: 3324785Swnj default: 33327063Skarels break; 3344785Swnj } 33527063Skarels 33627194Skarels raw: 337*54716Ssklower rip_input(m); 3389185Ssam return; 33927063Skarels 34040684Skarels freeit: 34127063Skarels m_freem(m); 3424785Swnj } 3434785Swnj 3444785Swnj /* 3454785Swnj * Reflect the ip packet back to the source 3464785Swnj */ 34740684Skarels icmp_reflect(m) 34840684Skarels struct mbuf *m; 3494785Swnj { 35040684Skarels register struct ip *ip = mtod(m, struct ip *); 35118373Skarels register struct in_ifaddr *ia; 35224811Skarels struct in_addr t; 35328926Skarels struct mbuf *opts = 0, *ip_srcroute(); 35427063Skarels int optlen = (ip->ip_hl << 2) - sizeof(struct ip); 3554785Swnj 3566583Ssam t = ip->ip_dst; 35724811Skarels ip->ip_dst = ip->ip_src; 35824811Skarels /* 35924811Skarels * If the incoming packet was addressed directly to us, 36024811Skarels * use dst as the src for the reply. Otherwise (broadcast 36124811Skarels * or anonymous), use the address which corresponds 36224811Skarels * to the incoming interface. 36324811Skarels */ 36424811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) { 36524811Skarels if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) 36618373Skarels break; 36724811Skarels if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && 36824811Skarels t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr) 36924811Skarels break; 37024811Skarels } 37124811Skarels if (ia == (struct in_ifaddr *)0) 37240684Skarels ia = ifptoia(m->m_pkthdr.rcvif); 37328344Skarels if (ia == (struct in_ifaddr *)0) 37428344Skarels ia = in_ifaddr; 37528344Skarels t = IA_SIN(ia)->sin_addr; 37624811Skarels ip->ip_src = t; 37730971Skarels ip->ip_ttl = MAXTTL; 37824811Skarels 37927063Skarels if (optlen > 0) { 38036817Skarels register u_char *cp; 38140684Skarels int opt, cnt; 38236817Skarels u_int len; 38336817Skarels 38426034Skarels /* 38536817Skarels * Retrieve any source routing from the incoming packet; 38636817Skarels * add on any record-route or timestamp options. 38726034Skarels */ 38836817Skarels cp = (u_char *) (ip + 1); 38936948Skarels if ((opts = ip_srcroute()) == 0 && 39040684Skarels (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) { 39136948Skarels opts->m_len = sizeof(struct in_addr); 39236948Skarels mtod(opts, struct in_addr *)->s_addr = 0; 39336948Skarels } 39436948Skarels if (opts) { 39536948Skarels #ifdef ICMPPRINTFS 39636948Skarels if (icmpprintfs) 39736948Skarels printf("icmp_reflect optlen %d rt %d => ", 39836948Skarels optlen, opts->m_len); 39936948Skarels #endif 40036817Skarels for (cnt = optlen; cnt > 0; cnt -= len, cp += len) { 40136948Skarels opt = cp[IPOPT_OPTVAL]; 40236948Skarels if (opt == IPOPT_EOL) 40336948Skarels break; 40436948Skarels if (opt == IPOPT_NOP) 40536948Skarels len = 1; 40636948Skarels else { 40736948Skarels len = cp[IPOPT_OLEN]; 40836948Skarels if (len <= 0 || len > cnt) 40936948Skarels break; 41036948Skarels } 41136948Skarels /* 41236948Skarels * should check for overflow, but it "can't happen" 41336948Skarels */ 41436948Skarels if (opt == IPOPT_RR || opt == IPOPT_TS) { 41536948Skarels bcopy((caddr_t)cp, 41636948Skarels mtod(opts, caddr_t) + opts->m_len, len); 41736948Skarels opts->m_len += len; 41836948Skarels } 41936948Skarels } 42036948Skarels if (opts->m_len % 4 != 0) { 42136948Skarels *(mtod(opts, caddr_t) + opts->m_len) = IPOPT_EOL; 42236948Skarels opts->m_len++; 42336948Skarels } 42436948Skarels #ifdef ICMPPRINTFS 42536948Skarels if (icmpprintfs) 42636948Skarels printf("%d\n", opts->m_len); 42736948Skarels #endif 42836817Skarels } 42936948Skarels /* 43036948Skarels * Now strip out original options by copying rest of first 43136948Skarels * mbuf's data back, and adjust the IP length. 43236948Skarels */ 43326034Skarels ip->ip_len -= optlen; 43436817Skarels ip->ip_hl = sizeof(struct ip) >> 2; 43536948Skarels m->m_len -= optlen; 43640684Skarels if (m->m_flags & M_PKTHDR) 43740684Skarels m->m_pkthdr.len -= optlen; 43836948Skarels optlen += sizeof(struct ip); 43936948Skarels bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1), 44040684Skarels (unsigned)(m->m_len - sizeof(struct ip))); 44124811Skarels } 44240684Skarels icmp_send(m, opts); 44326034Skarels if (opts) 44426383Skarels (void)m_free(opts); 4454785Swnj } 4464785Swnj 44724811Skarels struct in_ifaddr * 44824811Skarels ifptoia(ifp) 44924811Skarels struct ifnet *ifp; 45024811Skarels { 45124811Skarels register struct in_ifaddr *ia; 45224811Skarels 45324811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 45424811Skarels if (ia->ia_ifp == ifp) 45524811Skarels return (ia); 45624811Skarels return ((struct in_ifaddr *)0); 45724811Skarels } 45824811Skarels 4594785Swnj /* 4606583Ssam * Send an icmp packet back to the ip level, 4616583Ssam * after supplying a checksum. 4624785Swnj */ 46340684Skarels icmp_send(m, opts) 46440684Skarels register struct mbuf *m; 46526034Skarels struct mbuf *opts; 4664785Swnj { 46740684Skarels register struct ip *ip = mtod(m, struct ip *); 4689185Ssam register int hlen; 4696583Ssam register struct icmp *icp; 4706583Ssam 4719185Ssam hlen = ip->ip_hl << 2; 47240684Skarels m->m_data += hlen; 47327063Skarels m->m_len -= hlen; 4746583Ssam icp = mtod(m, struct icmp *); 4756583Ssam icp->icmp_cksum = 0; 4766583Ssam icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); 47740684Skarels m->m_data -= hlen; 4786583Ssam m->m_len += hlen; 47915027Smckusick #ifdef ICMPPRINTFS 4806590Ssam if (icmpprintfs) 4816590Ssam printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src); 48215027Smckusick #endif 48326034Skarels (void) ip_output(m, opts, (struct route *)0, 0); 4844785Swnj } 4854785Swnj 4864907Swnj n_time 4874923Swnj iptime() 4884801Swnj { 48925893Skarels struct timeval atv; 4904967Swnj u_long t; 4914801Swnj 49225893Skarels microtime(&atv); 49325893Skarels t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000; 4944907Swnj return (htonl(t)); 4954801Swnj } 496