123182Smckusick /* 240684Skarels * Copyright (c) 1982, 1986, 1989 Regents of the University of California. 332787Sbostic * All rights reserved. 423182Smckusick * 544478Sbostic * %sccs.include.redist.c% 632787Sbostic * 7*48463Skarels * @(#)ip_icmp.c 7.15 (Berkeley) 04/20/91 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 38*48463Skarels extern struct protosw inetsw[]; 39*48463Skarels 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 } 754785Swnj 764785Swnj /* 776583Ssam * First, formulate icmp message 784785Swnj */ 7940684Skarels m = m_gethdr(M_DONTWAIT, MT_HEADER); 8011532Ssam if (m == NULL) 8140684Skarels goto freeit; 8235794Skarels icmplen = oiplen + min(8, oip->ip_len); 8324811Skarels m->m_len = icmplen + ICMP_MINLEN; 8440684Skarels MH_ALIGN(m, m->m_len); 856583Ssam icp = mtod(m, struct icmp *); 8624811Skarels if ((u_int)type > ICMP_MAXTYPE) 8711532Ssam panic("icmp_error"); 8811532Ssam icmpstat.icps_outhist[type]++; 894785Swnj icp->icmp_type = type; 9024811Skarels if (type == ICMP_REDIRECT) 9124811Skarels icp->icmp_gwaddr = dest; 9224811Skarels else 9324811Skarels icp->icmp_void = 0; 944785Swnj if (type == ICMP_PARAMPROB) { 954785Swnj icp->icmp_pptr = code; 966583Ssam code = 0; 976583Ssam } 986583Ssam icp->icmp_code = code; 9924811Skarels bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen); 1006590Ssam nip = &icp->icmp_ip; 10135794Skarels nip->ip_len = htons((u_short)(nip->ip_len + oiplen)); 1024785Swnj 1034785Swnj /* 10436817Skarels * Now, copy old ip header (without options) 10536817Skarels * in front of icmp message. 1064785Swnj */ 10740684Skarels if (m->m_data - sizeof(struct ip) < m->m_pktdat) 10824811Skarels panic("icmp len"); 10940684Skarels m->m_data -= sizeof(struct ip); 11036817Skarels m->m_len += sizeof(struct ip); 11140684Skarels m->m_pkthdr.len = m->m_len; 11240684Skarels m->m_pkthdr.rcvif = n->m_pkthdr.rcvif; 1136583Ssam nip = mtod(m, struct ip *); 1146583Ssam bcopy((caddr_t)oip, (caddr_t)nip, oiplen); 11527063Skarels nip->ip_len = m->m_len; 11636817Skarels nip->ip_hl = sizeof(struct ip) >> 2; 1176583Ssam nip->ip_p = IPPROTO_ICMP; 11840684Skarels icmp_reflect(m); 1194785Swnj 12040684Skarels freeit: 12140684Skarels m_freem(n); 1224785Swnj } 1234785Swnj 1246583Ssam static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP }; 12540684Skarels static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET }; 12640684Skarels static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET }; 12740684Skarels static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET }; 12840684Skarels struct sockaddr_in icmpmask = { 8, 0 }; 12924811Skarels struct in_ifaddr *ifptoia(); 1306583Ssam 1314785Swnj /* 1324801Swnj * Process a received ICMP message. 1334785Swnj */ 13440684Skarels icmp_input(m, hlen) 13527194Skarels register struct mbuf *m; 13640684Skarels int hlen; 1374785Swnj { 1384801Swnj register struct icmp *icp; 1394801Swnj register struct ip *ip = mtod(m, struct ip *); 14040684Skarels int icmplen = ip->ip_len; 14116171Skarels register int i; 14224811Skarels struct in_ifaddr *ia; 14316171Skarels int (*ctlfunc)(), code; 1445172Swnj extern u_char ip_protox[]; 14518373Skarels extern struct in_addr in_makeaddr(); 1464785Swnj 1474785Swnj /* 1484785Swnj * Locate icmp structure in mbuf, and check 1494785Swnj * that not corrupted and of at least minimum length. 1504785Swnj */ 15115027Smckusick #ifdef ICMPPRINTFS 1526590Ssam if (icmpprintfs) 1536590Ssam printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen); 15415027Smckusick #endif 15515913Skarels if (icmplen < ICMP_MINLEN) { 15611532Ssam icmpstat.icps_tooshort++; 15740684Skarels goto freeit; 15811532Ssam } 15924811Skarels i = hlen + MIN(icmplen, ICMP_ADVLENMIN); 16040684Skarels if (m->m_len < i && (m = m_pullup(m, i)) == 0) { 16115831Skarels icmpstat.icps_tooshort++; 16215831Skarels return; 16315831Skarels } 16416171Skarels ip = mtod(m, struct ip *); 1654785Swnj m->m_len -= hlen; 16640684Skarels m->m_data += hlen; 1676583Ssam icp = mtod(m, struct icmp *); 16816171Skarels if (in_cksum(m, icmplen)) { 16911532Ssam icmpstat.icps_checksum++; 17040684Skarels goto freeit; 1716583Ssam } 17227063Skarels m->m_len += hlen; 17340684Skarels m->m_data -= hlen; 1744785Swnj 17515027Smckusick #ifdef ICMPPRINTFS 1764785Swnj /* 1774785Swnj * Message type specific processing. 1784785Swnj */ 1796590Ssam if (icmpprintfs) 1806590Ssam printf("icmp_input, type %d code %d\n", icp->icmp_type, 18115027Smckusick icp->icmp_code); 18215027Smckusick #endif 18324811Skarels if (icp->icmp_type > ICMP_MAXTYPE) 18427194Skarels goto raw; 18511532Ssam icmpstat.icps_inhist[icp->icmp_type]++; 18615027Smckusick code = icp->icmp_code; 18711532Ssam switch (icp->icmp_type) { 1884785Swnj 1894785Swnj case ICMP_UNREACH: 19015027Smckusick if (code > 5) 19115027Smckusick goto badcode; 19215027Smckusick code += PRC_UNREACH_NET; 19315027Smckusick goto deliver; 19415027Smckusick 1954785Swnj case ICMP_TIMXCEED: 19615027Smckusick if (code > 1) 19715027Smckusick goto badcode; 19815027Smckusick code += PRC_TIMXCEED_INTRANS; 19915027Smckusick goto deliver; 20015027Smckusick 2014785Swnj case ICMP_PARAMPROB: 20215027Smckusick if (code) 20315027Smckusick goto badcode; 20415027Smckusick code = PRC_PARAMPROB; 20515027Smckusick goto deliver; 20615027Smckusick 2074785Swnj case ICMP_SOURCEQUENCH: 20815027Smckusick if (code) 20915027Smckusick goto badcode; 21015027Smckusick code = PRC_QUENCH; 21115027Smckusick deliver: 2124785Swnj /* 21315027Smckusick * Problem with datagram; advise higher level routines. 2144785Swnj */ 21540684Skarels if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) || 21640684Skarels icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) { 21711532Ssam icmpstat.icps_badlen++; 21840684Skarels goto freeit; 21911532Ssam } 22040684Skarels NTOHS(icp->icmp_ip.ip_len); 22115027Smckusick #ifdef ICMPPRINTFS 2226590Ssam if (icmpprintfs) 2236590Ssam printf("deliver to protocol %d\n", icp->icmp_ip.ip_p); 22415027Smckusick #endif 22524811Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 2269030Sroot if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput) 22740684Skarels (*ctlfunc)(code, (struct sockaddr *)&icmpsrc, 22840684Skarels (caddr_t) &icp->icmp_ip); 22927063Skarels break; 2304785Swnj 23115027Smckusick badcode: 23215027Smckusick icmpstat.icps_badcode++; 23327063Skarels break; 23415027Smckusick 2354785Swnj case ICMP_ECHO: 2364785Swnj icp->icmp_type = ICMP_ECHOREPLY; 2374785Swnj goto reflect; 2384785Swnj 2394785Swnj case ICMP_TSTAMP: 24011532Ssam if (icmplen < ICMP_TSLEN) { 24111532Ssam icmpstat.icps_badlen++; 24227063Skarels break; 24311532Ssam } 2444785Swnj icp->icmp_type = ICMP_TSTAMPREPLY; 2454923Swnj icp->icmp_rtime = iptime(); 2464785Swnj icp->icmp_ttime = icp->icmp_rtime; /* bogus, do later! */ 2474785Swnj goto reflect; 2484785Swnj 2494785Swnj case ICMP_IREQ: 25018373Skarels #define satosin(sa) ((struct sockaddr_in *)(sa)) 25140684Skarels if (in_netof(ip->ip_src) == 0 && 25240684Skarels (ia = ifptoia(m->m_pkthdr.rcvif))) 25324811Skarels ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr), 25418373Skarels in_lnaof(ip->ip_src)); 25518373Skarels icp->icmp_type = ICMP_IREQREPLY; 2564785Swnj goto reflect; 2574785Swnj 25824811Skarels case ICMP_MASKREQ: 25940684Skarels if (icmplen < ICMP_MASKLEN || 26040684Skarels (ia = ifptoia(m->m_pkthdr.rcvif)) == 0) 26127063Skarels break; 26230362Skarels icp->icmp_type = ICMP_MASKREPLY; 26340684Skarels icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr; 26424811Skarels if (ip->ip_src.s_addr == 0) { 26524811Skarels if (ia->ia_ifp->if_flags & IFF_BROADCAST) 26624811Skarels ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr; 26724811Skarels else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) 26824811Skarels ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr; 26924811Skarels } 27027063Skarels reflect: 27127063Skarels ip->ip_len += hlen; /* since ip_input deducts this */ 27227063Skarels icmpstat.icps_reflect++; 27327063Skarels icmpstat.icps_outhist[icp->icmp_type]++; 27440684Skarels icmp_reflect(m); 27527063Skarels return; 27624811Skarels 27711549Ssam case ICMP_REDIRECT: 27811532Ssam if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) { 27911532Ssam icmpstat.icps_badlen++; 28027063Skarels break; 28111532Ssam } 28211549Ssam /* 28311549Ssam * Short circuit routing redirects to force 28411549Ssam * immediate change in the kernel's routing 28511549Ssam * tables. The message is also handed to anyone 28611549Ssam * listening on a raw socket (e.g. the routing 28717046Skarels * daemon for use in updating its tables). 28811549Ssam */ 28924811Skarels icmpgw.sin_addr = ip->ip_src; 29015831Skarels icmpdst.sin_addr = icp->icmp_gwaddr; 29124811Skarels #ifdef ICMPPRINTFS 29224811Skarels if (icmpprintfs) 29324811Skarels printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst, 29424811Skarels icp->icmp_gwaddr); 29524811Skarels #endif 29617046Skarels if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) { 29740684Skarels u_long in_netof(); 29817046Skarels icmpsrc.sin_addr = 29918373Skarels in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY); 30040684Skarels in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask); 30117046Skarels rtredirect((struct sockaddr *)&icmpsrc, 30240684Skarels (struct sockaddr *)&icmpdst, 30340684Skarels (struct sockaddr *)&icmpmask, RTF_GATEWAY, 30440684Skarels (struct sockaddr *)&icmpgw, (struct rtentry **)0); 30527194Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 30624811Skarels pfctlinput(PRC_REDIRECT_NET, 30724811Skarels (struct sockaddr *)&icmpsrc); 30817046Skarels } else { 30917046Skarels icmpsrc.sin_addr = icp->icmp_ip.ip_dst; 31017046Skarels rtredirect((struct sockaddr *)&icmpsrc, 31140684Skarels (struct sockaddr *)&icmpdst, 31240684Skarels (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST, 31340684Skarels (struct sockaddr *)&icmpgw, (struct rtentry **)0); 31424811Skarels pfctlinput(PRC_REDIRECT_HOST, 31524811Skarels (struct sockaddr *)&icmpsrc); 31617046Skarels } 31727063Skarels break; 31815831Skarels 31927063Skarels /* 32027063Skarels * No kernel processing for the following; 32127063Skarels * just fall through to send to raw listener. 32227063Skarels */ 32315831Skarels case ICMP_ECHOREPLY: 32415831Skarels case ICMP_TSTAMPREPLY: 32515831Skarels case ICMP_IREQREPLY: 32624811Skarels case ICMP_MASKREPLY: 3274785Swnj default: 32827063Skarels break; 3294785Swnj } 33027063Skarels 33127194Skarels raw: 33227063Skarels icmpsrc.sin_addr = ip->ip_src; 33327063Skarels icmpdst.sin_addr = ip->ip_dst; 33440684Skarels (void) raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc, 33527063Skarels (struct sockaddr *)&icmpdst); 3369185Ssam return; 33727063Skarels 33840684Skarels freeit: 33927063Skarels m_freem(m); 3404785Swnj } 3414785Swnj 3424785Swnj /* 3434785Swnj * Reflect the ip packet back to the source 3444785Swnj */ 34540684Skarels icmp_reflect(m) 34640684Skarels struct mbuf *m; 3474785Swnj { 34840684Skarels register struct ip *ip = mtod(m, struct ip *); 34918373Skarels register struct in_ifaddr *ia; 35024811Skarels struct in_addr t; 35128926Skarels struct mbuf *opts = 0, *ip_srcroute(); 35227063Skarels int optlen = (ip->ip_hl << 2) - sizeof(struct ip); 3534785Swnj 3546583Ssam t = ip->ip_dst; 35524811Skarels ip->ip_dst = ip->ip_src; 35624811Skarels /* 35724811Skarels * If the incoming packet was addressed directly to us, 35824811Skarels * use dst as the src for the reply. Otherwise (broadcast 35924811Skarels * or anonymous), use the address which corresponds 36024811Skarels * to the incoming interface. 36124811Skarels */ 36224811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) { 36324811Skarels if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) 36418373Skarels break; 36524811Skarels if ((ia->ia_ifp->if_flags & IFF_BROADCAST) && 36624811Skarels t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr) 36724811Skarels break; 36824811Skarels } 36924811Skarels if (ia == (struct in_ifaddr *)0) 37040684Skarels ia = ifptoia(m->m_pkthdr.rcvif); 37128344Skarels if (ia == (struct in_ifaddr *)0) 37228344Skarels ia = in_ifaddr; 37328344Skarels t = IA_SIN(ia)->sin_addr; 37424811Skarels ip->ip_src = t; 37530971Skarels ip->ip_ttl = MAXTTL; 37624811Skarels 37727063Skarels if (optlen > 0) { 37836817Skarels register u_char *cp; 37940684Skarels int opt, cnt; 38036817Skarels u_int len; 38136817Skarels 38226034Skarels /* 38336817Skarels * Retrieve any source routing from the incoming packet; 38436817Skarels * add on any record-route or timestamp options. 38526034Skarels */ 38636817Skarels cp = (u_char *) (ip + 1); 38736948Skarels if ((opts = ip_srcroute()) == 0 && 38840684Skarels (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) { 38936948Skarels opts->m_len = sizeof(struct in_addr); 39036948Skarels mtod(opts, struct in_addr *)->s_addr = 0; 39136948Skarels } 39236948Skarels if (opts) { 39336948Skarels #ifdef ICMPPRINTFS 39436948Skarels if (icmpprintfs) 39536948Skarels printf("icmp_reflect optlen %d rt %d => ", 39636948Skarels optlen, opts->m_len); 39736948Skarels #endif 39836817Skarels for (cnt = optlen; cnt > 0; cnt -= len, cp += len) { 39936948Skarels opt = cp[IPOPT_OPTVAL]; 40036948Skarels if (opt == IPOPT_EOL) 40136948Skarels break; 40236948Skarels if (opt == IPOPT_NOP) 40336948Skarels len = 1; 40436948Skarels else { 40536948Skarels len = cp[IPOPT_OLEN]; 40636948Skarels if (len <= 0 || len > cnt) 40736948Skarels break; 40836948Skarels } 40936948Skarels /* 41036948Skarels * should check for overflow, but it "can't happen" 41136948Skarels */ 41236948Skarels if (opt == IPOPT_RR || opt == IPOPT_TS) { 41336948Skarels bcopy((caddr_t)cp, 41436948Skarels mtod(opts, caddr_t) + opts->m_len, len); 41536948Skarels opts->m_len += len; 41636948Skarels } 41736948Skarels } 41836948Skarels if (opts->m_len % 4 != 0) { 41936948Skarels *(mtod(opts, caddr_t) + opts->m_len) = IPOPT_EOL; 42036948Skarels opts->m_len++; 42136948Skarels } 42236948Skarels #ifdef ICMPPRINTFS 42336948Skarels if (icmpprintfs) 42436948Skarels printf("%d\n", opts->m_len); 42536948Skarels #endif 42636817Skarels } 42736948Skarels /* 42836948Skarels * Now strip out original options by copying rest of first 42936948Skarels * mbuf's data back, and adjust the IP length. 43036948Skarels */ 43126034Skarels ip->ip_len -= optlen; 43236817Skarels ip->ip_hl = sizeof(struct ip) >> 2; 43336948Skarels m->m_len -= optlen; 43440684Skarels if (m->m_flags & M_PKTHDR) 43540684Skarels m->m_pkthdr.len -= optlen; 43636948Skarels optlen += sizeof(struct ip); 43736948Skarels bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1), 43840684Skarels (unsigned)(m->m_len - sizeof(struct ip))); 43924811Skarels } 44040684Skarels icmp_send(m, opts); 44126034Skarels if (opts) 44226383Skarels (void)m_free(opts); 4434785Swnj } 4444785Swnj 44524811Skarels struct in_ifaddr * 44624811Skarels ifptoia(ifp) 44724811Skarels struct ifnet *ifp; 44824811Skarels { 44924811Skarels register struct in_ifaddr *ia; 45024811Skarels 45124811Skarels for (ia = in_ifaddr; ia; ia = ia->ia_next) 45224811Skarels if (ia->ia_ifp == ifp) 45324811Skarels return (ia); 45424811Skarels return ((struct in_ifaddr *)0); 45524811Skarels } 45624811Skarels 4574785Swnj /* 4586583Ssam * Send an icmp packet back to the ip level, 4596583Ssam * after supplying a checksum. 4604785Swnj */ 46140684Skarels icmp_send(m, opts) 46240684Skarels register struct mbuf *m; 46326034Skarels struct mbuf *opts; 4644785Swnj { 46540684Skarels register struct ip *ip = mtod(m, struct ip *); 4669185Ssam register int hlen; 4676583Ssam register struct icmp *icp; 4686583Ssam 4699185Ssam hlen = ip->ip_hl << 2; 47040684Skarels m->m_data += hlen; 47127063Skarels m->m_len -= hlen; 4726583Ssam icp = mtod(m, struct icmp *); 4736583Ssam icp->icmp_cksum = 0; 4746583Ssam icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen); 47540684Skarels m->m_data -= hlen; 4766583Ssam m->m_len += hlen; 47715027Smckusick #ifdef ICMPPRINTFS 4786590Ssam if (icmpprintfs) 4796590Ssam printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src); 48015027Smckusick #endif 48126034Skarels (void) ip_output(m, opts, (struct route *)0, 0); 4824785Swnj } 4834785Swnj 4844907Swnj n_time 4854923Swnj iptime() 4864801Swnj { 48725893Skarels struct timeval atv; 4884967Swnj u_long t; 4894801Swnj 49025893Skarels microtime(&atv); 49125893Skarels t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000; 4924907Swnj return (htonl(t)); 4934801Swnj } 494