xref: /csrg-svn/sys/netinet/ip_icmp.c (revision 26383)
1 /*
2  * Copyright (c) 1982 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)ip_icmp.c	6.16 (Berkeley) 02/23/86
7  */
8 
9 #include "param.h"
10 #include "systm.h"
11 #include "mbuf.h"
12 #include "protosw.h"
13 #include "socket.h"
14 #include "time.h"
15 #include "kernel.h"
16 
17 #include "../net/route.h"
18 #include "../net/if.h"
19 
20 #include "in.h"
21 #include "in_systm.h"
22 #include "in_var.h"
23 #include "ip.h"
24 #include "ip_icmp.h"
25 #include "icmp_var.h"
26 
27 #ifdef ICMPPRINTFS
28 /*
29  * ICMP routines: error generation, receive packet processing, and
30  * routines to turnaround packets back to the originator, and
31  * host table maintenance routines.
32  */
33 int	icmpprintfs = 0;
34 #endif
35 
36 /*
37  * Generate an error packet of type error
38  * in response to bad packet ip.
39  */
40 /*VARARGS4*/
41 icmp_error(oip, type, code, ifp, dest)
42 	struct ip *oip;
43 	int type, code;
44 	struct ifnet *ifp;
45 	struct in_addr dest;
46 {
47 	register unsigned oiplen = oip->ip_hl << 2;
48 	register struct icmp *icp;
49 	struct mbuf *m;
50 	struct ip *nip;
51 	unsigned icmplen;
52 
53 #ifdef ICMPPRINTFS
54 	if (icmpprintfs)
55 		printf("icmp_error(%x, %d, %d)\n", oip, type, code);
56 #endif
57 	if (type != ICMP_REDIRECT)
58 		icmpstat.icps_error++;
59 	/*
60 	 * Don't send error if not the first fragment of message.
61 	 * Don't EVER error if the old packet protocol was ICMP.
62 	 * (Could do ECHO, etc, but not error indications.)
63 	 */
64 	if (oip->ip_off &~ (IP_MF|IP_DF))
65 		goto free;
66 	if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT) {
67 		icmpstat.icps_oldicmp++;
68 		goto free;
69 	}
70 
71 	/*
72 	 * First, formulate icmp message
73 	 */
74 	m = m_get(M_DONTWAIT, MT_HEADER);
75 	if (m == NULL)
76 		goto free;
77 	icmplen = oiplen + MIN(8, oip->ip_len);
78 	m->m_len = icmplen + ICMP_MINLEN;
79 	m->m_off = MMAXOFF - m->m_len;
80 	icp = mtod(m, struct icmp *);
81 	if ((u_int)type > ICMP_MAXTYPE)
82 		panic("icmp_error");
83 	icmpstat.icps_outhist[type]++;
84 	icp->icmp_type = type;
85 	if (type == ICMP_REDIRECT)
86 		icp->icmp_gwaddr = dest;
87 	else
88 		icp->icmp_void = 0;
89 	if (type == ICMP_PARAMPROB) {
90 		icp->icmp_pptr = code;
91 		code = 0;
92 	}
93 	icp->icmp_code = code;
94 	bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
95 	nip = &icp->icmp_ip;
96 	nip->ip_len += oiplen;
97 	nip->ip_len = htons((u_short)nip->ip_len);
98 
99 	/*
100 	 * Now, copy old ip header in front of icmp
101 	 * message.  This allows us to reuse any source
102 	 * routing info present.
103 	 */
104 	if (m->m_len + oiplen > MLEN)
105 		oiplen = sizeof(struct ip);
106 	if (m->m_len + oiplen > MLEN)
107 		panic("icmp len");
108 	m->m_off -= oiplen;
109 	nip = mtod(m, struct ip *);
110 	bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
111 	nip->ip_len = m->m_len + oiplen;
112 	nip->ip_p = IPPROTO_ICMP;
113 	/* icmp_send adds ip header to m_off and m_len, so we deduct here */
114 	m->m_off += oiplen;
115 	icmp_reflect(nip, ifp);
116 
117 free:
118 	m_freem(dtom(oip));
119 }
120 
121 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
122 static struct sockaddr_in icmpsrc = { AF_INET };
123 static struct sockaddr_in icmpdst = { AF_INET };
124 static struct sockaddr_in icmpgw = { AF_INET };
125 struct in_ifaddr *ifptoia();
126 
127 /*
128  * Process a received ICMP message.
129  */
130 icmp_input(m, ifp)
131 	struct mbuf *m;
132 	struct ifnet *ifp;
133 {
134 	register struct icmp *icp;
135 	register struct ip *ip = mtod(m, struct ip *);
136 	int icmplen = ip->ip_len, hlen = ip->ip_hl << 2;
137 	register int i;
138 	struct in_ifaddr *ia;
139 	int (*ctlfunc)(), code;
140 	extern u_char ip_protox[];
141 	extern struct in_addr in_makeaddr();
142 
143 	/*
144 	 * Locate icmp structure in mbuf, and check
145 	 * that not corrupted and of at least minimum length.
146 	 */
147 #ifdef ICMPPRINTFS
148 	if (icmpprintfs)
149 		printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
150 #endif
151 	if (icmplen < ICMP_MINLEN) {
152 		icmpstat.icps_tooshort++;
153 		goto free;
154 	}
155 	i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
156  	if ((m->m_off > MMAXOFF || m->m_len < i) &&
157  		(m = m_pullup(m, i)) == 0)  {
158 		icmpstat.icps_tooshort++;
159 		return;
160 	}
161  	ip = mtod(m, struct ip *);
162 	m->m_len -= hlen;
163 	m->m_off += hlen;
164 	icp = mtod(m, struct icmp *);
165 	if (in_cksum(m, icmplen)) {
166 		icmpstat.icps_checksum++;
167 		goto free;
168 	}
169 
170 #ifdef ICMPPRINTFS
171 	/*
172 	 * Message type specific processing.
173 	 */
174 	if (icmpprintfs)
175 		printf("icmp_input, type %d code %d\n", icp->icmp_type,
176 		    icp->icmp_code);
177 #endif
178 	if (icp->icmp_type > ICMP_MAXTYPE)
179 		goto free;
180 	icmpstat.icps_inhist[icp->icmp_type]++;
181 	code = icp->icmp_code;
182 	switch (icp->icmp_type) {
183 
184 	case ICMP_UNREACH:
185 		if (code > 5)
186 			goto badcode;
187 		code += PRC_UNREACH_NET;
188 		goto deliver;
189 
190 	case ICMP_TIMXCEED:
191 		if (code > 1)
192 			goto badcode;
193 		code += PRC_TIMXCEED_INTRANS;
194 		goto deliver;
195 
196 	case ICMP_PARAMPROB:
197 		if (code)
198 			goto badcode;
199 		code = PRC_PARAMPROB;
200 		goto deliver;
201 
202 	case ICMP_SOURCEQUENCH:
203 		if (code)
204 			goto badcode;
205 		code = PRC_QUENCH;
206 	deliver:
207 		/*
208 		 * Problem with datagram; advise higher level routines.
209 		 */
210 		icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
211 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
212 			icmpstat.icps_badlen++;
213 			goto free;
214 		}
215 #ifdef ICMPPRINTFS
216 		if (icmpprintfs)
217 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
218 #endif
219 		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
220 		if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
221 			(*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
222 		goto free;
223 
224 	badcode:
225 		icmpstat.icps_badcode++;
226 		goto free;
227 
228 	case ICMP_ECHO:
229 		icp->icmp_type = ICMP_ECHOREPLY;
230 		goto reflect;
231 
232 	case ICMP_TSTAMP:
233 		if (icmplen < ICMP_TSLEN) {
234 			icmpstat.icps_badlen++;
235 			goto free;
236 		}
237 		icp->icmp_type = ICMP_TSTAMPREPLY;
238 		icp->icmp_rtime = iptime();
239 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
240 		goto reflect;
241 
242 	case ICMP_IREQ:
243 #define	satosin(sa)	((struct sockaddr_in *)(sa))
244 		if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp)))
245 			ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
246 			    in_lnaof(ip->ip_src));
247 		icp->icmp_type = ICMP_IREQREPLY;
248 		goto reflect;
249 
250 	case ICMP_MASKREQ:
251 		if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0)
252 			goto free;
253 		icp->icmp_type = ICMP_IREQREPLY;
254 		icp->icmp_mask = ia->ia_netmask;
255 		if (ip->ip_src.s_addr == 0) {
256 			if (ia->ia_ifp->if_flags & IFF_BROADCAST)
257 			    ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
258 			else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
259 			    ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
260 		}
261 		goto reflect;
262 
263 	case ICMP_REDIRECT:
264 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
265 			icmpstat.icps_badlen++;
266 			goto free;
267 		}
268 		/*
269 		 * Short circuit routing redirects to force
270 		 * immediate change in the kernel's routing
271 		 * tables.  The message is also handed to anyone
272 		 * listening on a raw socket (e.g. the routing
273 		 * daemon for use in updating its tables).
274 		 */
275 		icmpgw.sin_addr = ip->ip_src;
276 		icmpdst.sin_addr = icp->icmp_gwaddr;
277 #ifdef	ICMPPRINTFS
278 		if (icmpprintfs)
279 			printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
280 				icp->icmp_gwaddr);
281 #endif
282 		if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
283 			icmpsrc.sin_addr =
284 			 in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
285 			rtredirect((struct sockaddr *)&icmpsrc,
286 			  (struct sockaddr *)&icmpdst, RTF_GATEWAY,
287 			  (struct sockaddr *)&icmpgw);
288 			pfctlinput(PRC_REDIRECT_NET,
289 			  (struct sockaddr *)&icmpsrc);
290 		} else {
291 			icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
292 			rtredirect((struct sockaddr *)&icmpsrc,
293 			  (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST,
294 			  (struct sockaddr *)&icmpgw);
295 			pfctlinput(PRC_REDIRECT_HOST,
296 			  (struct sockaddr *)&icmpsrc);
297 		}
298 		/* FALL THROUGH */
299 
300 	case ICMP_ECHOREPLY:
301 	case ICMP_TSTAMPREPLY:
302 	case ICMP_IREQREPLY:
303 	case ICMP_MASKREPLY:
304 		icmpsrc.sin_addr = ip->ip_src;
305 		icmpdst.sin_addr = ip->ip_dst;
306 		raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
307 		  (struct sockaddr *)&icmpdst);
308 		return;
309 
310 	default:
311 		goto free;
312 	}
313 reflect:
314 	ip->ip_len += hlen;		/* since ip_input deducts this */
315 	icmpstat.icps_reflect++;
316 	icmpstat.icps_outhist[icp->icmp_type]++;
317 	icmp_reflect(ip, ifp);
318 	return;
319 free:
320 	m_freem(dtom(ip));
321 }
322 
323 /*
324  * Reflect the ip packet back to the source
325  */
326 icmp_reflect(ip, ifp)
327 	register struct ip *ip;
328 	struct ifnet *ifp;
329 {
330 	register struct in_ifaddr *ia;
331 	struct in_addr t;
332 	struct mbuf *m, *opts = 0, *ip_srcroute();
333 
334 	t = ip->ip_dst;
335 	ip->ip_dst = ip->ip_src;
336 	/*
337 	 * If the incoming packet was addressed directly to us,
338 	 * use dst as the src for the reply.  Otherwise (broadcast
339 	 * or anonymous), use the address which corresponds
340 	 * to the incoming interface.
341 	 */
342 	for (ia = in_ifaddr; ia; ia = ia->ia_next) {
343 		if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
344 			break;
345 		if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
346 		    t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
347 			break;
348 	}
349 	if (ia == (struct in_ifaddr *)0)
350 		ia = ifptoia(ifp);
351 	if (ia)
352 		t = IA_SIN(ia)->sin_addr;
353 	ip->ip_src = t;
354 
355 	if ((ip->ip_hl << 2) > sizeof(struct ip)) {
356 		int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
357 
358 		/*
359 		 * Retrieve any source routing from the incoming packet
360 		 * and strip out other options.  Adjust the IP
361 		 * and mbuf lengths.  The length of the options is added
362 		 * to the mbuf here, as it was already subtracted
363 		 * in icmp_input, and ip_stripoptions will subtract it again.
364 		 * Adjust the mbuf offset to point to the new location
365 		 * of the icmp header.
366 		 */
367 		opts = ip_srcroute();
368 		m = dtom(ip);
369 		m->m_off -= optlen;
370 		m->m_len += optlen;
371 		ip->ip_len -= optlen;
372 		ip_stripoptions(ip, (struct mbuf *)0);
373 	}
374 	icmp_send(ip, opts);
375 	if (opts)
376 		(void)m_free(opts);
377 }
378 
379 struct in_ifaddr *
380 ifptoia(ifp)
381 	struct ifnet *ifp;
382 {
383 	register struct in_ifaddr *ia;
384 
385 	for (ia = in_ifaddr; ia; ia = ia->ia_next)
386 		if (ia->ia_ifp == ifp)
387 			return (ia);
388 	return ((struct in_ifaddr *)0);
389 }
390 
391 /*
392  * Send an icmp packet back to the ip level,
393  * after supplying a checksum.
394  */
395 icmp_send(ip, opts)
396 	register struct ip *ip;
397 	struct mbuf *opts;
398 {
399 	register int hlen;
400 	register struct icmp *icp;
401 	register struct mbuf *m;
402 
403 	m = dtom(ip);
404 	hlen = ip->ip_hl << 2;
405 	icp = mtod(m, struct icmp *);
406 	icp->icmp_cksum = 0;
407 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
408 	m->m_off -= hlen;
409 	m->m_len += hlen;
410 #ifdef ICMPPRINTFS
411 	if (icmpprintfs)
412 		printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
413 #endif
414 	(void) ip_output(m, opts, (struct route *)0, 0);
415 }
416 
417 n_time
418 iptime()
419 {
420 	struct timeval atv;
421 	u_long t;
422 
423 	microtime(&atv);
424 	t = (atv.tv_sec % (24*60*60)) * 1000 + atv.tv_usec / 1000;
425 	return (htonl(t));
426 }
427