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