Home | History | Annotate | Line # | Download | only in netinet
ip_icmp.c revision 1.3
      1 /*
      2  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  *
     33  *	from: @(#)ip_icmp.c	7.15 (Berkeley) 4/20/91
     34  *	$Id: ip_icmp.c,v 1.3 1993/12/06 04:59:34 hpeyerl Exp $
     35  */
     36 
     37 #include "param.h"
     38 #include "systm.h"
     39 #include "malloc.h"
     40 #include "mbuf.h"
     41 #include "protosw.h"
     42 #include "socket.h"
     43 #include "time.h"
     44 #include "kernel.h"
     45 
     46 #include "../net/route.h"
     47 #include "../net/if.h"
     48 
     49 #include "in.h"
     50 #include "in_systm.h"
     51 #include "in_var.h"
     52 #include "ip.h"
     53 #include "ip_icmp.h"
     54 #include "icmp_var.h"
     55 
     56 /*
     57  * ICMP routines: error generation, receive packet processing, and
     58  * routines to turnaround packets back to the originator, and
     59  * host table maintenance routines.
     60  */
     61 #ifdef ICMPPRINTFS
     62 int	icmpprintfs = 0;
     63 #endif
     64 
     65 extern	struct protosw inetsw[];
     66 
     67 /*
     68  * Generate an error packet of type error
     69  * in response to bad packet ip.
     70  */
     71 /*VARARGS3*/
     72 icmp_error(n, type, code, dest)
     73 	struct mbuf *n;
     74 	int type, code;
     75 	struct in_addr dest;
     76 {
     77 	register struct ip *oip = mtod(n, struct ip *), *nip;
     78 	register unsigned oiplen = oip->ip_hl << 2;
     79 	register struct icmp *icp;
     80 	register struct mbuf *m;
     81 	unsigned icmplen;
     82 
     83 #ifdef ICMPPRINTFS
     84 	if (icmpprintfs)
     85 		printf("icmp_error(%x, %d, %d)\n", oip, type, code);
     86 #endif
     87 	if (type != ICMP_REDIRECT)
     88 		icmpstat.icps_error++;
     89 	/*
     90 	 * Don't send error if not the first fragment of message.
     91 	 * Don't error if the old packet protocol was ICMP
     92 	 * error message, only known informational types.
     93 	 */
     94 	if (oip->ip_off &~ (IP_MF|IP_DF))
     95 		goto freeit;
     96 	if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
     97 	  n->m_len >= oiplen + ICMP_MINLEN &&
     98 	  !ICMP_INFOTYPE(((struct icmp *)((caddr_t)oip + oiplen))->icmp_type)) {
     99 		icmpstat.icps_oldicmp++;
    100 		goto freeit;
    101 	}
    102 #ifdef MULTICAST
    103 	/* Don't send error in response to a multicast or broadcast packet */
    104 	if (n->m_flags & (M_MCAST | M_BCAST))
    105 		goto freeit;
    106 #endif
    107 	/*
    108 	 * First, formulate icmp message
    109 	 */
    110 	m = m_gethdr(M_DONTWAIT, MT_HEADER);
    111 	if (m == NULL)
    112 		goto freeit;
    113 	icmplen = oiplen + min(8, oip->ip_len);
    114 	m->m_len = icmplen + ICMP_MINLEN;
    115 	MH_ALIGN(m, m->m_len);
    116 	icp = mtod(m, struct icmp *);
    117 	if ((u_int)type > ICMP_MAXTYPE)
    118 		panic("icmp_error");
    119 	icmpstat.icps_outhist[type]++;
    120 	icp->icmp_type = type;
    121 	if (type == ICMP_REDIRECT)
    122 		icp->icmp_gwaddr = dest;
    123 	else
    124 		icp->icmp_void = 0;
    125 	if (type == ICMP_PARAMPROB) {
    126 		icp->icmp_pptr = code;
    127 		code = 0;
    128 	}
    129 	icp->icmp_code = code;
    130 	bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
    131 	nip = &icp->icmp_ip;
    132 	nip->ip_len = htons((u_short)(nip->ip_len + oiplen));
    133 
    134 	/*
    135 	 * Now, copy old ip header (without options)
    136 	 * in front of icmp message.
    137 	 */
    138 	if (m->m_data - sizeof(struct ip) < m->m_pktdat)
    139 		panic("icmp len");
    140 	m->m_data -= sizeof(struct ip);
    141 	m->m_len += sizeof(struct ip);
    142 	m->m_pkthdr.len = m->m_len;
    143 	m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
    144 	nip = mtod(m, struct ip *);
    145 	bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
    146 	nip->ip_len = m->m_len;
    147 	nip->ip_hl = sizeof(struct ip) >> 2;
    148 	nip->ip_p = IPPROTO_ICMP;
    149 	icmp_reflect(m);
    150 
    151 freeit:
    152 	m_freem(n);
    153 }
    154 
    155 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
    156 static struct sockaddr_in icmpsrc = { sizeof (struct sockaddr_in), AF_INET };
    157 static struct sockaddr_in icmpdst = { sizeof (struct sockaddr_in), AF_INET };
    158 static struct sockaddr_in icmpgw = { sizeof (struct sockaddr_in), AF_INET };
    159 struct sockaddr_in icmpmask = { 8, 0 };
    160 struct in_ifaddr *ifptoia();
    161 
    162 /*
    163  * Process a received ICMP message.
    164  */
    165 icmp_input(m, hlen)
    166 	register struct mbuf *m;
    167 	int hlen;
    168 {
    169 	register struct icmp *icp;
    170 	register struct ip *ip = mtod(m, struct ip *);
    171 	int icmplen = ip->ip_len;
    172 	register int i;
    173 	struct in_ifaddr *ia;
    174 	int (*ctlfunc)(), code;
    175 	extern u_char ip_protox[];
    176 	extern struct in_addr in_makeaddr();
    177 
    178 	/*
    179 	 * Locate icmp structure in mbuf, and check
    180 	 * that not corrupted and of at least minimum length.
    181 	 */
    182 #ifdef ICMPPRINTFS
    183 	if (icmpprintfs)
    184 		printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
    185 #endif
    186 	if (icmplen < ICMP_MINLEN) {
    187 		icmpstat.icps_tooshort++;
    188 		goto freeit;
    189 	}
    190 	i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
    191  	if (m->m_len < i && (m = m_pullup(m, i)) == 0)  {
    192 		icmpstat.icps_tooshort++;
    193 		return;
    194 	}
    195  	ip = mtod(m, struct ip *);
    196 	m->m_len -= hlen;
    197 	m->m_data += hlen;
    198 	icp = mtod(m, struct icmp *);
    199 	if (in_cksum(m, icmplen)) {
    200 		icmpstat.icps_checksum++;
    201 		goto freeit;
    202 	}
    203 	m->m_len += hlen;
    204 	m->m_data -= hlen;
    205 
    206 #ifdef ICMPPRINTFS
    207 	/*
    208 	 * Message type specific processing.
    209 	 */
    210 	if (icmpprintfs)
    211 		printf("icmp_input, type %d code %d\n", icp->icmp_type,
    212 		    icp->icmp_code);
    213 #endif
    214 	if (icp->icmp_type > ICMP_MAXTYPE)
    215 		goto raw;
    216 	icmpstat.icps_inhist[icp->icmp_type]++;
    217 	code = icp->icmp_code;
    218 	switch (icp->icmp_type) {
    219 
    220 	case ICMP_UNREACH:
    221 		if (code > 5)
    222 			goto badcode;
    223 		code += PRC_UNREACH_NET;
    224 		goto deliver;
    225 
    226 	case ICMP_TIMXCEED:
    227 		if (code > 1)
    228 			goto badcode;
    229 		code += PRC_TIMXCEED_INTRANS;
    230 		goto deliver;
    231 
    232 	case ICMP_PARAMPROB:
    233 		if (code)
    234 			goto badcode;
    235 		code = PRC_PARAMPROB;
    236 		goto deliver;
    237 
    238 	case ICMP_SOURCEQUENCH:
    239 		if (code)
    240 			goto badcode;
    241 		code = PRC_QUENCH;
    242 	deliver:
    243 		/*
    244 		 * Problem with datagram; advise higher level routines.
    245 		 */
    246 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp) ||
    247 		    icp->icmp_ip.ip_hl < (sizeof(struct ip) >> 2)) {
    248 			icmpstat.icps_badlen++;
    249 			goto freeit;
    250 		}
    251 		NTOHS(icp->icmp_ip.ip_len);
    252 #ifdef ICMPPRINTFS
    253 		if (icmpprintfs)
    254 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
    255 #endif
    256 		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
    257 		if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
    258 			(*ctlfunc)(code, (struct sockaddr *)&icmpsrc,
    259 			    (caddr_t) &icp->icmp_ip);
    260 		break;
    261 
    262 	badcode:
    263 		icmpstat.icps_badcode++;
    264 		break;
    265 
    266 	case ICMP_ECHO:
    267 		icp->icmp_type = ICMP_ECHOREPLY;
    268 		goto reflect;
    269 
    270 	case ICMP_TSTAMP:
    271 		if (icmplen < ICMP_TSLEN) {
    272 			icmpstat.icps_badlen++;
    273 			break;
    274 		}
    275 		icp->icmp_type = ICMP_TSTAMPREPLY;
    276 		icp->icmp_rtime = iptime();
    277 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
    278 		goto reflect;
    279 
    280 	case ICMP_IREQ:
    281 #define	satosin(sa)	((struct sockaddr_in *)(sa))
    282 		if (in_netof(ip->ip_src) == 0 &&
    283 		    (ia = ifptoia(m->m_pkthdr.rcvif)))
    284 			ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
    285 			    in_lnaof(ip->ip_src));
    286 		icp->icmp_type = ICMP_IREQREPLY;
    287 		goto reflect;
    288 
    289 	case ICMP_MASKREQ:
    290 		if (icmplen < ICMP_MASKLEN ||
    291 		    (ia = ifptoia(m->m_pkthdr.rcvif)) == 0)
    292 			break;
    293 		icp->icmp_type = ICMP_MASKREPLY;
    294 		icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr;
    295 		if (ip->ip_src.s_addr == 0) {
    296 			if (ia->ia_ifp->if_flags & IFF_BROADCAST)
    297 			    ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
    298 			else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
    299 			    ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
    300 		}
    301 reflect:
    302 		ip->ip_len += hlen;	/* since ip_input deducts this */
    303 		icmpstat.icps_reflect++;
    304 		icmpstat.icps_outhist[icp->icmp_type]++;
    305 		icmp_reflect(m);
    306 		return;
    307 
    308 	case ICMP_REDIRECT:
    309 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
    310 			icmpstat.icps_badlen++;
    311 			break;
    312 		}
    313 		/*
    314 		 * Short circuit routing redirects to force
    315 		 * immediate change in the kernel's routing
    316 		 * tables.  The message is also handed to anyone
    317 		 * listening on a raw socket (e.g. the routing
    318 		 * daemon for use in updating its tables).
    319 		 */
    320 		icmpgw.sin_addr = ip->ip_src;
    321 		icmpdst.sin_addr = icp->icmp_gwaddr;
    322 #ifdef	ICMPPRINTFS
    323 		if (icmpprintfs)
    324 			printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
    325 				icp->icmp_gwaddr);
    326 #endif
    327 		if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
    328 			u_long in_netof();
    329 			icmpsrc.sin_addr =
    330 			 in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
    331 			in_sockmaskof(icp->icmp_ip.ip_dst, &icmpmask);
    332 			rtredirect((struct sockaddr *)&icmpsrc,
    333 			  (struct sockaddr *)&icmpdst,
    334 			  (struct sockaddr *)&icmpmask, RTF_GATEWAY,
    335 			  (struct sockaddr *)&icmpgw, (struct rtentry **)0);
    336 			icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
    337 			pfctlinput(PRC_REDIRECT_NET,
    338 			  (struct sockaddr *)&icmpsrc);
    339 		} else {
    340 			icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
    341 			rtredirect((struct sockaddr *)&icmpsrc,
    342 			  (struct sockaddr *)&icmpdst,
    343 			  (struct sockaddr *)0, RTF_GATEWAY | RTF_HOST,
    344 			  (struct sockaddr *)&icmpgw, (struct rtentry **)0);
    345 			pfctlinput(PRC_REDIRECT_HOST,
    346 			  (struct sockaddr *)&icmpsrc);
    347 		}
    348 		break;
    349 
    350 	/*
    351 	 * No kernel processing for the following;
    352 	 * just fall through to send to raw listener.
    353 	 */
    354 	case ICMP_ECHOREPLY:
    355 	case ICMP_TSTAMPREPLY:
    356 	case ICMP_IREQREPLY:
    357 	case ICMP_MASKREPLY:
    358 	default:
    359 		break;
    360 	}
    361 
    362 raw:
    363 	icmpsrc.sin_addr = ip->ip_src;
    364 	icmpdst.sin_addr = ip->ip_dst;
    365 	(void) raw_input(m, &icmproto, (struct sockaddr *)&icmpsrc,
    366 	    (struct sockaddr *)&icmpdst);
    367 	return;
    368 
    369 freeit:
    370 	m_freem(m);
    371 }
    372 
    373 /*
    374  * Reflect the ip packet back to the source
    375  */
    376 icmp_reflect(m)
    377 	struct mbuf *m;
    378 {
    379 	register struct ip *ip = mtod(m, struct ip *);
    380 	register struct in_ifaddr *ia;
    381 	struct in_addr t;
    382 	struct mbuf *opts = 0, *ip_srcroute();
    383 	int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
    384 
    385 	t = ip->ip_dst;
    386 	ip->ip_dst = ip->ip_src;
    387 	/*
    388 	 * If the incoming packet was addressed directly to us,
    389 	 * use dst as the src for the reply.  Otherwise (broadcast
    390 	 * or anonymous), use the address which corresponds
    391 	 * to the incoming interface.
    392 	 */
    393 	for (ia = in_ifaddr; ia; ia = ia->ia_next) {
    394 		if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
    395 			break;
    396 		if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
    397 		    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
    398 			break;
    399 	}
    400 	if (ia == (struct in_ifaddr *)0)
    401 		ia = ifptoia(m->m_pkthdr.rcvif);
    402 	if (ia == (struct in_ifaddr *)0)
    403 		ia = in_ifaddr;
    404 	t = IA_SIN(ia)->sin_addr;
    405 	ip->ip_src = t;
    406 	ip->ip_ttl = MAXTTL;
    407 
    408 	if (optlen > 0) {
    409 		register u_char *cp;
    410 		int opt, cnt;
    411 		u_int len;
    412 
    413 		/*
    414 		 * Retrieve any source routing from the incoming packet;
    415 		 * add on any record-route or timestamp options.
    416 		 */
    417 		cp = (u_char *) (ip + 1);
    418 		if ((opts = ip_srcroute()) == 0 &&
    419 		    (opts = m_gethdr(M_DONTWAIT, MT_HEADER))) {
    420 			opts->m_len = sizeof(struct in_addr);
    421 			mtod(opts, struct in_addr *)->s_addr = 0;
    422 		}
    423 		if (opts) {
    424 #ifdef ICMPPRINTFS
    425 		    if (icmpprintfs)
    426 			    printf("icmp_reflect optlen %d rt %d => ",
    427 				optlen, opts->m_len);
    428 #endif
    429 		    for (cnt = optlen; cnt > 0; cnt -= len, cp += len) {
    430 			    opt = cp[IPOPT_OPTVAL];
    431 			    if (opt == IPOPT_EOL)
    432 				    break;
    433 			    if (opt == IPOPT_NOP)
    434 				    len = 1;
    435 			    else {
    436 				    len = cp[IPOPT_OLEN];
    437 				    if (len <= 0 || len > cnt)
    438 					    break;
    439 			    }
    440 			    /*
    441 			     * should check for overflow, but it "can't happen"
    442 			     */
    443 			    if (opt == IPOPT_RR || opt == IPOPT_TS) {
    444 				    bcopy((caddr_t)cp,
    445 					mtod(opts, caddr_t) + opts->m_len, len);
    446 				    opts->m_len += len;
    447 			    }
    448 		    }
    449 		    if (opts->m_len % 4 != 0) {
    450 			    *(mtod(opts, caddr_t) + opts->m_len) = IPOPT_EOL;
    451 			    opts->m_len++;
    452 		    }
    453 #ifdef ICMPPRINTFS
    454 		    if (icmpprintfs)
    455 			    printf("%d\n", opts->m_len);
    456 #endif
    457 		}
    458 		/*
    459 		 * Now strip out original options by copying rest of first
    460 		 * mbuf's data back, and adjust the IP length.
    461 		 */
    462 		ip->ip_len -= optlen;
    463 		ip->ip_hl = sizeof(struct ip) >> 2;
    464 		m->m_len -= optlen;
    465 		if (m->m_flags & M_PKTHDR)
    466 			m->m_pkthdr.len -= optlen;
    467 		optlen += sizeof(struct ip);
    468 		bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
    469 			 (unsigned)(m->m_len - sizeof(struct ip)));
    470 	}
    471 	m->m_flags &= ~(M_BCAST|M_MCAST);
    472 	icmp_send(m, opts);
    473 	if (opts)
    474 		(void)m_free(opts);
    475 }
    476 
    477 struct in_ifaddr *
    478 ifptoia(ifp)
    479 	struct ifnet *ifp;
    480 {
    481 	register struct in_ifaddr *ia;
    482 
    483 	for (ia = in_ifaddr; ia; ia = ia->ia_next)
    484 		if (ia->ia_ifp == ifp)
    485 			return (ia);
    486 	return ((struct in_ifaddr *)0);
    487 }
    488 
    489 /*
    490  * Send an icmp packet back to the ip level,
    491  * after supplying a checksum.
    492  */
    493 icmp_send(m, opts)
    494 	register struct mbuf *m;
    495 	struct mbuf *opts;
    496 {
    497 	register struct ip *ip = mtod(m, struct ip *);
    498 	register int hlen;
    499 	register struct icmp *icp;
    500 
    501 	hlen = ip->ip_hl << 2;
    502 	m->m_data += hlen;
    503 	m->m_len -= hlen;
    504 	icp = mtod(m, struct icmp *);
    505 	icp->icmp_cksum = 0;
    506 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
    507 	m->m_data -= hlen;
    508 	m->m_len += hlen;
    509 #ifdef ICMPPRINTFS
    510 	if (icmpprintfs)
    511 		printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
    512 #endif
    513 	(void) ip_output(m, opts, (struct route *)0, 0);
    514 }
    515 
    516 n_time
    517 iptime()
    518 {
    519 	struct timeval atv;
    520 	u_long t;
    521 
    522 	microtime(&atv);
    523 	t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
    524 	return (htonl(t));
    525 }
    526