xref: /csrg-svn/sys/netinet/ip_icmp.c (revision 28926)
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