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