xref: /original-bsd/sys/netinet/ip_icmp.c (revision 9c06e5c6)
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.13 (Berkeley) 09/16/85
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 /*VARARGS3*/
41 icmp_error(oip, type, code, dest)
42 	struct ip *oip;
43 	int type, code;
44 	struct in_addr dest;
45 {
46 	register unsigned oiplen = oip->ip_hl << 2;
47 	register struct icmp *icp;
48 	struct mbuf *m;
49 	struct ip *nip;
50 	int icmplen;
51 
52 #ifdef ICMPPRINTFS
53 	if (icmpprintfs)
54 		printf("icmp_error(%x, %d, %d)\n", oip, type, code);
55 #endif
56 	icmpstat.icps_error++;
57 	/*
58 	 * Don't send error if not the first fragment of message.
59 	 * Don't EVER error if the old packet protocol was ICMP.
60 	 * (Could do ECHO, etc, but not error indications.)
61 	 */
62 	if (oip->ip_off &~ (IP_MF|IP_DF))
63 		goto free;
64 	if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT) {
65 		icmpstat.icps_oldicmp++;
66 		goto free;
67 	}
68 
69 	/*
70 	 * First, formulate icmp message
71 	 */
72 	m = m_get(M_DONTWAIT, MT_HEADER);
73 	if (m == NULL)
74 		goto free;
75 	icmplen = oiplen + MIN(8, oip->ip_len);
76 	m->m_len = icmplen + ICMP_MINLEN;
77 	m->m_off = MMAXOFF - m->m_len;
78 	icp = mtod(m, struct icmp *);
79 	if ((u_int)type > ICMP_MAXTYPE)
80 		panic("icmp_error");
81 	icmpstat.icps_outhist[type]++;
82 	icp->icmp_type = type;
83 	if (type == ICMP_REDIRECT)
84 		icp->icmp_gwaddr = dest;
85 	else
86 		icp->icmp_void = 0;
87 	if (type == ICMP_PARAMPROB) {
88 		icp->icmp_pptr = code;
89 		code = 0;
90 	}
91 	icp->icmp_code = code;
92 	bcopy((caddr_t)oip, (caddr_t)&icp->icmp_ip, icmplen);
93 	nip = &icp->icmp_ip;
94 	nip->ip_len += oiplen;
95 	nip->ip_len = htons((u_short)nip->ip_len);
96 
97 	/*
98 	 * Now, copy old ip header in front of icmp
99 	 * message.  This allows us to reuse any source
100 	 * routing info present.
101 	 */
102 	if (m->m_len + oiplen > MLEN)
103 		oiplen = sizeof(struct ip);
104 	if (m->m_len + oiplen > MLEN)
105 		panic("icmp len");
106 	m->m_off -= oiplen;
107 	nip = mtod(m, struct ip *);
108 	bcopy((caddr_t)oip, (caddr_t)nip, oiplen);
109 	nip->ip_len = m->m_len + oiplen;
110 	nip->ip_p = IPPROTO_ICMP;
111 	/* icmp_send adds ip header to m_off and m_len, so we deduct here */
112 	m->m_off += oiplen;
113 	icmp_reflect(nip, in_ifaddr->ia_ifp);
114 
115 free:
116 	m_freem(dtom(oip));
117 }
118 
119 static struct sockproto icmproto = { AF_INET, IPPROTO_ICMP };
120 static struct sockaddr_in icmpsrc = { AF_INET };
121 static struct sockaddr_in icmpdst = { AF_INET };
122 static struct sockaddr_in icmpgw = { AF_INET };
123 struct in_ifaddr *ifptoia();
124 
125 /*
126  * Process a received ICMP message.
127  */
128 icmp_input(m, ifp)
129 	struct mbuf *m;
130 	struct ifnet *ifp;
131 {
132 	register struct icmp *icp;
133 	register struct ip *ip = mtod(m, struct ip *);
134 	int icmplen = ip->ip_len, hlen = ip->ip_hl << 2;
135 	register int i;
136 	struct in_ifaddr *ia;
137 	int (*ctlfunc)(), code;
138 	extern u_char ip_protox[];
139 	extern struct in_addr in_makeaddr();
140 
141 	/*
142 	 * Locate icmp structure in mbuf, and check
143 	 * that not corrupted and of at least minimum length.
144 	 */
145 #ifdef ICMPPRINTFS
146 	if (icmpprintfs)
147 		printf("icmp_input from %x, len %d\n", ip->ip_src, icmplen);
148 #endif
149 	if (icmplen < ICMP_MINLEN) {
150 		icmpstat.icps_tooshort++;
151 		goto free;
152 	}
153 	i = hlen + MIN(icmplen, ICMP_ADVLENMIN);
154  	if ((m->m_off > MMAXOFF || m->m_len < i) &&
155  		(m = m_pullup(m, i)) == 0)  {
156 		icmpstat.icps_tooshort++;
157 		return;
158 	}
159  	ip = mtod(m, struct ip *);
160 	m->m_len -= hlen;
161 	m->m_off += hlen;
162 	icp = mtod(m, struct icmp *);
163 	if (in_cksum(m, icmplen)) {
164 		icmpstat.icps_checksum++;
165 		goto free;
166 	}
167 
168 #ifdef ICMPPRINTFS
169 	/*
170 	 * Message type specific processing.
171 	 */
172 	if (icmpprintfs)
173 		printf("icmp_input, type %d code %d\n", icp->icmp_type,
174 		    icp->icmp_code);
175 #endif
176 	if (icp->icmp_type > ICMP_MAXTYPE)
177 		goto free;
178 	icmpstat.icps_inhist[icp->icmp_type]++;
179 	code = icp->icmp_code;
180 	switch (icp->icmp_type) {
181 
182 	case ICMP_UNREACH:
183 		if (code > 5)
184 			goto badcode;
185 		code += PRC_UNREACH_NET;
186 		goto deliver;
187 
188 	case ICMP_TIMXCEED:
189 		if (code > 1)
190 			goto badcode;
191 		code += PRC_TIMXCEED_INTRANS;
192 		goto deliver;
193 
194 	case ICMP_PARAMPROB:
195 		if (code)
196 			goto badcode;
197 		code = PRC_PARAMPROB;
198 		goto deliver;
199 
200 	case ICMP_SOURCEQUENCH:
201 		if (code)
202 			goto badcode;
203 		code = PRC_QUENCH;
204 	deliver:
205 		/*
206 		 * Problem with datagram; advise higher level routines.
207 		 */
208 		icp->icmp_ip.ip_len = ntohs((u_short)icp->icmp_ip.ip_len);
209 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
210 			icmpstat.icps_badlen++;
211 			goto free;
212 		}
213 #ifdef ICMPPRINTFS
214 		if (icmpprintfs)
215 			printf("deliver to protocol %d\n", icp->icmp_ip.ip_p);
216 #endif
217 		icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
218 		if (ctlfunc = inetsw[ip_protox[icp->icmp_ip.ip_p]].pr_ctlinput)
219 			(*ctlfunc)(code, (struct sockaddr *)&icmpsrc);
220 		goto free;
221 
222 	badcode:
223 		icmpstat.icps_badcode++;
224 		goto free;
225 
226 	case ICMP_ECHO:
227 		icp->icmp_type = ICMP_ECHOREPLY;
228 		goto reflect;
229 
230 	case ICMP_TSTAMP:
231 		if (icmplen < ICMP_TSLEN) {
232 			icmpstat.icps_badlen++;
233 			goto free;
234 		}
235 		icp->icmp_type = ICMP_TSTAMPREPLY;
236 		icp->icmp_rtime = iptime();
237 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
238 		goto reflect;
239 
240 	case ICMP_IREQ:
241 #define	satosin(sa)	((struct sockaddr_in *)(sa))
242 		if (in_netof(ip->ip_src) == 0 && (ia = ifptoia(ifp)))
243 			ip->ip_src = in_makeaddr(in_netof(IA_SIN(ia)->sin_addr),
244 			    in_lnaof(ip->ip_src));
245 		icp->icmp_type = ICMP_IREQREPLY;
246 		goto reflect;
247 
248 	case ICMP_MASKREQ:
249 		if (icmplen < ICMP_MASKLEN || (ia = ifptoia(ifp)) == 0)
250 			goto free;
251 		icp->icmp_type = ICMP_IREQREPLY;
252 		icp->icmp_mask = ia->ia_netmask;
253 		if (ip->ip_src.s_addr == 0) {
254 			if (ia->ia_ifp->if_flags & IFF_BROADCAST)
255 			    ip->ip_src = satosin(&ia->ia_broadaddr)->sin_addr;
256 			else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT)
257 			    ip->ip_src = satosin(&ia->ia_dstaddr)->sin_addr;
258 		}
259 		goto reflect;
260 
261 	case ICMP_REDIRECT:
262 		if (icmplen < ICMP_ADVLENMIN || icmplen < ICMP_ADVLEN(icp)) {
263 			icmpstat.icps_badlen++;
264 			goto free;
265 		}
266 		/*
267 		 * Short circuit routing redirects to force
268 		 * immediate change in the kernel's routing
269 		 * tables.  The message is also handed to anyone
270 		 * listening on a raw socket (e.g. the routing
271 		 * daemon for use in updating its tables).
272 		 */
273 		icmpgw.sin_addr = ip->ip_src;
274 		icmpdst.sin_addr = icp->icmp_gwaddr;
275 #ifdef	ICMPPRINTFS
276 		if (icmpprintfs)
277 			printf("redirect dst %x to %x\n", icp->icmp_ip.ip_dst,
278 				icp->icmp_gwaddr);
279 #endif
280 		if (code == ICMP_REDIRECT_NET || code == ICMP_REDIRECT_TOSNET) {
281 			icmpsrc.sin_addr =
282 			 in_makeaddr(in_netof(icp->icmp_ip.ip_dst), INADDR_ANY);
283 			rtredirect((struct sockaddr *)&icmpsrc,
284 			  (struct sockaddr *)&icmpdst, RTF_GATEWAY,
285 			  (struct sockaddr *)&icmpgw);
286 			pfctlinput(PRC_REDIRECT_NET,
287 			  (struct sockaddr *)&icmpsrc);
288 		} else {
289 			icmpsrc.sin_addr = icp->icmp_ip.ip_dst;
290 			rtredirect((struct sockaddr *)&icmpsrc,
291 			  (struct sockaddr *)&icmpdst, RTF_GATEWAY | RTF_HOST,
292 			  (struct sockaddr *)&icmpgw);
293 			pfctlinput(PRC_REDIRECT_HOST,
294 			  (struct sockaddr *)&icmpsrc);
295 		}
296 		/* FALL THROUGH */
297 
298 	case ICMP_ECHOREPLY:
299 	case ICMP_TSTAMPREPLY:
300 	case ICMP_IREQREPLY:
301 	case ICMP_MASKREPLY:
302 		icmpsrc.sin_addr = ip->ip_src;
303 		icmpdst.sin_addr = ip->ip_dst;
304 		raw_input(dtom(icp), &icmproto, (struct sockaddr *)&icmpsrc,
305 		  (struct sockaddr *)&icmpdst);
306 		return;
307 
308 	default:
309 		goto free;
310 	}
311 reflect:
312 	ip->ip_len += hlen;		/* since ip_input deducts this */
313 	icmpstat.icps_reflect++;
314 	icmpstat.icps_outhist[icp->icmp_type]++;
315 	icmp_reflect(ip, ifp);
316 	return;
317 free:
318 	m_freem(dtom(ip));
319 }
320 
321 /*
322  * Reflect the ip packet back to the source
323  */
324 icmp_reflect(ip, ifp)
325 	struct ip *ip;
326 	struct ifnet *ifp;
327 {
328 	register struct in_ifaddr *ia;
329 	register u_char *cp;
330 	int opt, optlen, cnt;
331 	struct in_addr t;
332 	struct in_addr *p, *q;
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 	/*
356 	 * If the incoming packet was source-routed,
357 	 * we need to reverse the route and set the next-hop destination.
358 	 * We can dispense with the error checking
359 	 * as ip_dooptions has been through here before.
360 	 */
361 	if ((ip->ip_hl << 2) > sizeof(struct ip)) {
362 		cp = (u_char *)(ip + 1);
363 		cnt = (ip->ip_hl << 2) - sizeof (struct ip);
364 		for (; cnt > 0; cnt -= optlen, cp += optlen) {
365 			opt = cp[IPOPT_OPTVAL];
366 			if (opt == IPOPT_EOL)
367 				break;
368 			if (opt == IPOPT_NOP)
369 				optlen = 1;
370 			else
371 				optlen = cp[IPOPT_OLEN];
372 			if (opt == IPOPT_LSRR || opt == IPOPT_SSRR) {
373 				p = (struct in_addr *)
374 				    (cp + cp[IPOPT_OFFSET] - 1) - 1;
375 				q = (struct in_addr *)(cp + IPOPT_MINOFF - 1);
376 				/*
377 				 * First switch the last route entry
378 				 * (first hop for return) with ip_dst
379 				 * (final hop on return).
380 				 */
381 				bcopy((caddr_t)p, (caddr_t)&t, sizeof(t));
382 				bcopy((caddr_t)&ip->ip_dst, (caddr_t)p,
383 				   sizeof(struct in_addr));
384 				ip->ip_dst = t;
385 				p--;
386 				/*
387 				 * Then reverse remaining route entries.
388 				 */
389 				while (p > q) {
390 					bcopy((caddr_t)p, (caddr_t)&t,
391 					    sizeof(struct in_addr));
392 					bcopy((caddr_t)q, (caddr_t)p,
393 					    sizeof(struct in_addr));
394 					bcopy((caddr_t)&t, (caddr_t)q,
395 					    sizeof(struct in_addr));
396 					p--;
397 					q++;
398 				}
399 				cp[IPOPT_OFFSET] = IPOPT_MINOFF;
400 				break;
401 			}
402 		}
403 	}
404 	icmp_send(ip);
405 }
406 
407 struct in_ifaddr *
408 ifptoia(ifp)
409 	struct ifnet *ifp;
410 {
411 	register struct in_ifaddr *ia;
412 
413 	for (ia = in_ifaddr; ia; ia = ia->ia_next)
414 		if (ia->ia_ifp == ifp)
415 			return (ia);
416 	return ((struct in_ifaddr *)0);
417 }
418 
419 /*
420  * Send an icmp packet back to the ip level,
421  * after supplying a checksum.
422  */
423 icmp_send(ip)
424 	struct ip *ip;
425 {
426 	register int hlen;
427 	register struct icmp *icp;
428 	register struct mbuf *m;
429 
430 	m = dtom(ip);
431 	hlen = ip->ip_hl << 2;
432 	icp = mtod(m, struct icmp *);
433 	icp->icmp_cksum = 0;
434 	icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen);
435 	m->m_off -= hlen;
436 	m->m_len += hlen;
437 #ifdef ICMPPRINTFS
438 	if (icmpprintfs)
439 		printf("icmp_send dst %x src %x\n", ip->ip_dst, ip->ip_src);
440 #endif
441 	(void) ip_output(m, (struct mbuf *)0, (struct route *)0, 0);
442 }
443 
444 n_time
445 iptime()
446 {
447 	int s = spl6();
448 	u_long t;
449 
450 	t = (time.tv_sec % (24*60*60)) * 1000 + time.tv_usec / 1000;
451 	splx(s);
452 	return (htonl(t));
453 }
454