. service tells you which device it couldn't stat
[minix3.git] / servers / inet / generic / icmp.c
bloba24507b1695b275f900f6f941bec58f4cab9ff8d
1 /*
2 icmp.c
4 Copyright 1995 Philip Homburg
5 */
7 #include "inet.h"
8 #include "buf.h"
9 #include "event.h"
10 #include "type.h"
12 #include "assert.h"
13 #include "clock.h"
14 #include "icmp.h"
15 #include "icmp_lib.h"
16 #include "io.h"
17 #include "ip.h"
18 #include "ip_int.h"
19 #include "ipr.h"
21 THIS_FILE
23 typedef struct icmp_port
25 int icp_flags;
26 int icp_state;
27 int icp_ipport;
28 int icp_ipfd;
29 unsigned icp_rate_count;
30 unsigned icp_rate_report;
31 time_t icp_rate_lasttime;
32 acc_t *icp_head_queue;
33 acc_t *icp_tail_queue;
34 acc_t *icp_write_pack;
35 event_t icp_event;
36 } icmp_port_t;
38 #define ICPF_EMPTY 0x0
39 #define ICPF_SUSPEND 0x1
40 #define ICPF_READ_IP 0x2
41 #define ICPF_READ_SP 0x4
42 #define ICPF_WRITE_IP 0x8
43 #define ICPF_WRITE_SP 0x10
45 #define ICPS_BEGIN 0
46 #define ICPS_IPOPT 1
47 #define ICPS_MAIN 2
48 #define ICPS_ERROR 3
50 PRIVATE icmp_port_t *icmp_port_table;
52 FORWARD void icmp_main ARGS(( icmp_port_t *icmp_port ));
53 FORWARD acc_t *icmp_getdata ARGS(( int port, size_t offset,
54 size_t count, int for_ioctl ));
55 FORWARD int icmp_putdata ARGS(( int port, size_t offset,
56 acc_t *data, int for_ioctl ));
57 FORWARD void icmp_read ARGS(( icmp_port_t *icmp_port ));
58 FORWARD void process_data ARGS(( icmp_port_t *icmp_port,
59 acc_t *data ));
60 FORWARD u16_t icmp_pack_oneCsum ARGS(( acc_t *ip_pack ));
61 FORWARD void icmp_echo_request ARGS(( icmp_port_t *icmp_port,
62 acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr,
63 acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr ));
64 FORWARD void icmp_dst_unreach ARGS(( icmp_port_t *icmp_port,
65 acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr,
66 acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr ));
67 FORWARD void icmp_time_exceeded ARGS(( icmp_port_t *icmp_port,
68 acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr,
69 acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr ));
70 FORWARD void icmp_router_advertisement ARGS(( icmp_port_t *icmp_port,
71 acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr ));
72 FORWARD void icmp_redirect ARGS(( icmp_port_t *icmp_port,
73 ip_hdr_t *ip_hdr, acc_t *icmp_pack, int icmp_len,
74 icmp_hdr_t *icmp_hdr ));
75 FORWARD acc_t *make_repl_ip ARGS(( ip_hdr_t *ip_hdr,
76 int ip_len ));
77 FORWARD void enqueue_pack ARGS(( icmp_port_t *icmp_port,
78 acc_t *reply_ip_hdr ));
79 FORWARD int icmp_rate_limit ARGS(( icmp_port_t *icmp_port,
80 acc_t *reply_ip_hdr ));
81 FORWARD void icmp_write ARGS(( event_t *ev, ev_arg_t ev_arg ));
82 FORWARD void icmp_buffree ARGS(( int priority ));
83 FORWARD acc_t *icmp_err_pack ARGS(( acc_t *pack, icmp_hdr_t **icmp_hdr_pp ));
84 #ifdef BUF_CONSISTENCY_CHECK
85 FORWARD void icmp_bufcheck ARGS(( void ));
86 #endif
88 PUBLIC void icmp_prep()
90 icmp_port_table= alloc(ip_conf_nr * sizeof(icmp_port_table[0]));
93 PUBLIC void icmp_init()
95 int i;
96 icmp_port_t *icmp_port;
98 assert (BUF_S >= sizeof (nwio_ipopt_t));
100 for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++)
102 icmp_port->icp_flags= ICPF_EMPTY;
103 icmp_port->icp_state= ICPS_BEGIN;
104 icmp_port->icp_ipport= i;
105 icmp_port->icp_rate_count= 0;
106 icmp_port->icp_rate_report= ICMP_MAX_RATE;
107 icmp_port->icp_rate_lasttime= 0;
108 ev_init(&icmp_port->icp_event);
111 #ifndef BUF_CONSISTENCY_CHECK
112 bf_logon(icmp_buffree);
113 #else
114 bf_logon(icmp_buffree, icmp_bufcheck);
115 #endif
117 for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++)
119 icmp_main (icmp_port);
123 PRIVATE void icmp_main(icmp_port)
124 icmp_port_t *icmp_port;
126 int result;
127 switch (icmp_port->icp_state)
129 case ICPS_BEGIN:
130 icmp_port->icp_head_queue= 0;
131 icmp_port->icp_ipfd= ip_open(icmp_port->icp_ipport,
132 icmp_port->icp_ipport, icmp_getdata, icmp_putdata,
133 0 /* no put_pkt */, 0 /* no select_res */);
134 if (icmp_port->icp_ipfd<0)
136 DBLOCK(1, printf("unable to open ip_port %d\n",
137 icmp_port->icp_ipport));
138 break;
140 icmp_port->icp_state= ICPS_IPOPT;
141 icmp_port->icp_flags &= ~ICPF_SUSPEND;
142 result= ip_ioctl (icmp_port->icp_ipfd, NWIOSIPOPT);
143 if (result == NW_SUSPEND)
145 icmp_port->icp_flags |= ICPF_SUSPEND;
146 break;
148 assert(result == NW_OK);
150 /* falls through */
151 case ICPS_IPOPT:
152 icmp_port->icp_state= ICPS_MAIN;
153 icmp_port->icp_flags &= ~ICPF_SUSPEND;
154 icmp_read(icmp_port);
155 break;
156 default:
157 DBLOCK(1, printf("unknown state %d\n",
158 icmp_port->icp_state));
159 break;
163 PRIVATE acc_t *icmp_getdata(port, offset, count, for_ioctl)
164 int port;
165 size_t offset, count;
166 int for_ioctl;
168 icmp_port_t *icmp_port;
169 nwio_ipopt_t *ipopt;
170 acc_t *data;
171 int result;
172 ev_arg_t ev_arg;
174 icmp_port= &icmp_port_table[port];
176 if (icmp_port->icp_flags & ICPF_WRITE_IP)
178 if (!count)
180 bf_afree(icmp_port->icp_write_pack);
181 icmp_port->icp_write_pack= 0;
183 result= (int)offset;
184 if (result<0)
186 DBLOCK(1, printf("got write error %d\n",
187 result));
189 if (icmp_port->icp_flags & ICPF_WRITE_SP)
191 icmp_port->icp_flags &= ~ICPF_WRITE_SP;
192 ev_arg.ev_ptr= icmp_port;
193 ev_enqueue(&icmp_port->icp_event, icmp_write,
194 ev_arg);
196 return NW_OK;
198 return bf_cut(icmp_port->icp_write_pack, offset, count);
200 switch (icmp_port->icp_state)
202 case ICPS_IPOPT:
203 if (!count)
205 result= (int)offset;
206 assert(result == NW_OK);
207 if (result < 0)
209 icmp_port->icp_state= ICPS_ERROR;
210 break;
212 if (icmp_port->icp_flags & ICPF_SUSPEND)
213 icmp_main(icmp_port);
214 return NW_OK;
217 data= bf_memreq (sizeof (*ipopt));
218 ipopt= (nwio_ipopt_t *)ptr2acc_data(data);
219 ipopt->nwio_flags= NWIO_COPY | NWIO_EN_LOC |
220 NWIO_EN_BROAD |
221 NWIO_REMANY | NWIO_PROTOSPEC |
222 NWIO_HDR_O_ANY | NWIO_RWDATALL;
223 ipopt->nwio_proto= IPPROTO_ICMP;
224 return data;
225 default:
226 break;
228 DBLOCK(1, printf("unknown state %d\n", icmp_port->icp_state));
229 return NULL;
232 PRIVATE int icmp_putdata(port, offset, data, for_ioctl)
233 int port;
234 size_t offset;
235 acc_t *data;
236 int for_ioctl;
238 icmp_port_t *icmp_port;
239 int result;
241 icmp_port= &icmp_port_table[port];
243 if (icmp_port->icp_flags & ICPF_READ_IP)
245 if (!data)
247 result= (int)offset;
248 if (result<0)
250 DBLOCK(1, printf("got read error %d\n",
251 result));
253 if (icmp_port->icp_flags & ICPF_READ_SP)
255 icmp_port->icp_flags &=
256 ~(ICPF_READ_IP|ICPF_READ_SP);
257 icmp_read (icmp_port);
259 return NW_OK;
261 process_data(icmp_port, data);
262 return NW_OK;
264 switch (icmp_port->icp_state)
266 default:
267 DBLOCK(1, printf("unknown state %d\n",
268 icmp_port->icp_state));
269 return 0;
273 PRIVATE void icmp_read(icmp_port)
274 icmp_port_t *icmp_port;
276 int result;
278 for (;;)
280 icmp_port->icp_flags |= ICPF_READ_IP;
281 icmp_port->icp_flags &= ~ICPF_READ_SP;
283 result= ip_read(icmp_port->icp_ipfd, ICMP_MAX_DATAGRAM);
284 if (result == NW_SUSPEND)
286 icmp_port->icp_flags |= ICPF_READ_SP;
287 return;
292 PUBLIC void icmp_snd_time_exceeded(port_nr, pack, code)
293 int port_nr;
294 acc_t *pack;
295 int code;
297 icmp_hdr_t *icmp_hdr;
298 icmp_port_t *icmp_port;
300 if (port_nr >= 0 && port_nr < ip_conf_nr)
301 icmp_port= &icmp_port_table[port_nr];
302 else
304 printf("icmp_snd_time_exceeded: strange port %d\n", port_nr);
305 bf_afree(pack);
306 return;
308 pack= icmp_err_pack(pack, &icmp_hdr);
309 if (pack == NULL)
310 return;
311 icmp_hdr->ih_type= ICMP_TYPE_TIME_EXCEEDED;
312 icmp_hdr->ih_code= code;
313 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
314 (u16_t *)&icmp_hdr->ih_type, 2);
315 enqueue_pack(icmp_port, pack);
318 PUBLIC void icmp_snd_redirect(port_nr, pack, code, gw)
319 int port_nr;
320 acc_t *pack;
321 int code;
322 ipaddr_t gw;
324 icmp_hdr_t *icmp_hdr;
325 icmp_port_t *icmp_port;
327 if (port_nr >= 0 && port_nr < ip_conf_nr)
328 icmp_port= &icmp_port_table[port_nr];
329 else
331 printf("icmp_snd_redirect: strange port %d\n", port_nr);
332 bf_afree(pack);
333 return;
335 pack= icmp_err_pack(pack, &icmp_hdr);
336 if (pack == NULL)
337 return;
338 icmp_hdr->ih_type= ICMP_TYPE_REDIRECT;
339 icmp_hdr->ih_code= code;
340 icmp_hdr->ih_hun.ihh_gateway= gw;
341 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
342 (u16_t *)&icmp_hdr->ih_type, 2);
343 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
344 (u16_t *)&icmp_hdr->ih_hun.ihh_gateway, 4);
345 enqueue_pack(icmp_port, pack);
348 PUBLIC void icmp_snd_unreachable(port_nr, pack, code)
349 int port_nr;
350 acc_t *pack;
351 int code;
353 icmp_hdr_t *icmp_hdr;
354 icmp_port_t *icmp_port;
356 if (port_nr >= 0 && port_nr < ip_conf_nr)
357 icmp_port= &icmp_port_table[port_nr];
358 else
360 printf("icmp_snd_unreachable: strange port %d\n", port_nr);
361 bf_afree(pack);
362 return;
364 pack= icmp_err_pack(pack, &icmp_hdr);
365 if (pack == NULL)
366 return;
367 icmp_hdr->ih_type= ICMP_TYPE_DST_UNRCH;
368 icmp_hdr->ih_code= code;
369 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
370 (u16_t *)&icmp_hdr->ih_type, 2);
371 enqueue_pack(icmp_port, pack);
374 PUBLIC void icmp_snd_mtu(port_nr, pack, mtu)
375 int port_nr;
376 acc_t *pack;
377 u16_t mtu;
379 icmp_hdr_t *icmp_hdr;
380 icmp_port_t *icmp_port;
382 if (port_nr >= 0 && port_nr < ip_conf_nr)
383 icmp_port= &icmp_port_table[port_nr];
384 else
386 printf("icmp_snd_mtu: strange port %d\n", port_nr);
387 bf_afree(pack);
388 return;
391 pack= icmp_err_pack(pack, &icmp_hdr);
392 if (pack == NULL)
393 return;
394 icmp_hdr->ih_type= ICMP_TYPE_DST_UNRCH;
395 icmp_hdr->ih_code= ICMP_FRAGM_AND_DF;
396 icmp_hdr->ih_hun.ihh_mtu.im_mtu= htons(mtu);
397 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
398 (u16_t *)&icmp_hdr->ih_type, 2);
399 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
400 (u16_t *)&icmp_hdr->ih_hun.ihh_mtu.im_mtu, 2);
401 enqueue_pack(icmp_port, pack);
404 PRIVATE void process_data(icmp_port, data)
405 icmp_port_t *icmp_port;
406 acc_t *data;
408 ip_hdr_t *ip_hdr;
409 icmp_hdr_t *icmp_hdr;
410 acc_t *icmp_data;
411 int ip_hdr_len;
412 size_t pack_len;
414 /* Align entire packet */
415 data= bf_align(data, BUF_S, 4);
417 data= bf_packIffLess(data, IP_MIN_HDR_SIZE);
418 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
419 DIFBLOCK(0x10, (ip_hdr->ih_dst & HTONL(0xf0000000)) == HTONL(0xe0000000),
420 printf("got multicast packet\n"));
421 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
423 if (ip_hdr_len>IP_MIN_HDR_SIZE)
425 data= bf_packIffLess(data, ip_hdr_len);
426 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
429 pack_len= bf_bufsize(data);
430 pack_len -= ip_hdr_len;
431 if (pack_len < ICMP_MIN_HDR_SIZE)
433 if (pack_len == 0 && ip_hdr->ih_proto == 0)
435 /* IP layer reports new ip address, which can be
436 * ignored.
439 else
440 DBLOCK(1, printf("got an incomplete icmp packet\n"));
441 bf_afree(data);
442 return;
445 icmp_data= bf_cut(data, ip_hdr_len, pack_len);
447 icmp_data= bf_packIffLess (icmp_data, ICMP_MIN_HDR_SIZE);
448 icmp_hdr= (icmp_hdr_t *)ptr2acc_data(icmp_data);
450 if ((u16_t)~icmp_pack_oneCsum(icmp_data))
452 DBLOCK(1, printf(
453 "got packet with bad checksum (= 0x%x, 0x%x)\n",
454 icmp_hdr->ih_chksum,
455 (u16_t)~icmp_pack_oneCsum(icmp_data)));
456 bf_afree(data);
457 bf_afree(icmp_data);
458 return;
461 switch (icmp_hdr->ih_type)
463 case ICMP_TYPE_ECHO_REPL:
464 break;
465 case ICMP_TYPE_DST_UNRCH:
466 icmp_dst_unreach (icmp_port, data, ip_hdr_len, ip_hdr,
467 icmp_data, pack_len, icmp_hdr);
468 break;
469 case ICMP_TYPE_SRC_QUENCH:
470 /* Ignore src quench ICMPs */
471 DBLOCK(2, printf("ignoring SRC QUENCH ICMP.\n"));
472 break;
473 case ICMP_TYPE_REDIRECT:
474 icmp_redirect (icmp_port, ip_hdr, icmp_data, pack_len,
475 icmp_hdr);
476 break;
477 case ICMP_TYPE_ECHO_REQ:
478 icmp_echo_request(icmp_port, data, ip_hdr_len, ip_hdr,
479 icmp_data, pack_len, icmp_hdr);
480 return;
481 case ICMP_TYPE_ROUTER_ADVER:
482 icmp_router_advertisement(icmp_port, icmp_data, pack_len,
483 icmp_hdr);
484 break;
485 case ICMP_TYPE_ROUTE_SOL:
486 break; /* Should be handled by a routing deamon. */
487 case ICMP_TYPE_TIME_EXCEEDED:
488 icmp_time_exceeded (icmp_port, data, ip_hdr_len, ip_hdr,
489 icmp_data, pack_len, icmp_hdr);
490 break;
491 default:
492 DBLOCK(1, printf("got an unknown icmp (%d) from ",
493 icmp_hdr->ih_type);
494 writeIpAddr(ip_hdr->ih_src); printf("\n"));
495 break;
497 bf_afree(data);
498 bf_afree(icmp_data);
501 PRIVATE void icmp_echo_request(icmp_port, ip_data, ip_len, ip_hdr,
502 icmp_data, icmp_len, icmp_hdr)
503 icmp_port_t *icmp_port;
504 acc_t *ip_data, *icmp_data;
505 int ip_len, icmp_len;
506 ip_hdr_t *ip_hdr;
507 icmp_hdr_t *icmp_hdr;
509 acc_t *repl_ip_hdr, *repl_icmp;
510 ipaddr_t tmpaddr, locaddr, netmask;
511 icmp_hdr_t *repl_icmp_hdr;
512 i32_t tmp_chksum;
513 ip_port_t *ip_port;
515 if (icmp_hdr->ih_code != 0)
517 DBLOCK(1,
518 printf("got an icmp echo request with unknown code (%d)\n",
519 icmp_hdr->ih_code));
520 bf_afree(ip_data);
521 bf_afree(icmp_data);
522 return;
524 if (icmp_len < ICMP_MIN_HDR_SIZE + sizeof(icmp_id_seq_t))
526 DBLOCK(1, printf("got an incomplete icmp echo request\n"));
527 bf_afree(ip_data);
528 bf_afree(icmp_data);
529 return;
531 tmpaddr= ntohl(ip_hdr->ih_dst);
532 if ((tmpaddr & 0xe0000000) == 0xe0000000 &&
533 tmpaddr != 0xffffffff)
535 /* Respond only to the all hosts multicast address until
536 * a decent listening service has been implemented
538 if (tmpaddr != 0xe0000001)
540 bf_afree(ip_data);
541 bf_afree(icmp_data);
542 return;
546 /* Limit subnet broadcasts to the local net */
547 ip_port= &ip_port_table[icmp_port->icp_ipport];
548 locaddr= ip_port->ip_ipaddr;
549 netmask= ip_port->ip_subnetmask;
550 if (ip_hdr->ih_dst == (locaddr | ~netmask) &&
551 (ip_port->ip_flags & IPF_SUBNET_BCAST) &&
552 ((ip_hdr->ih_src ^ locaddr) & netmask) != 0)
554 /* Directed broadcast */
555 bf_afree(ip_data);
556 bf_afree(icmp_data);
557 return;
560 repl_ip_hdr= make_repl_ip(ip_hdr, ip_len);
561 repl_icmp= bf_memreq (ICMP_MIN_HDR_SIZE);
562 repl_icmp_hdr= (icmp_hdr_t *)ptr2acc_data(repl_icmp);
563 repl_icmp_hdr->ih_type= ICMP_TYPE_ECHO_REPL;
564 repl_icmp_hdr->ih_code= 0;
566 DBLOCK(2,
567 printf("ih_chksum= 0x%x, ih_type= 0x%x, repl->ih_type= 0x%x\n",
568 icmp_hdr->ih_chksum, *(u16_t *)&icmp_hdr->ih_type,
569 *(u16_t *)&repl_icmp_hdr->ih_type));
570 tmp_chksum= (~icmp_hdr->ih_chksum & 0xffff) -
571 (i32_t)*(u16_t *)&icmp_hdr->ih_type+
572 *(u16_t *)&repl_icmp_hdr->ih_type;
573 tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff);
574 tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff);
575 repl_icmp_hdr->ih_chksum= ~tmp_chksum;
576 DBLOCK(2, printf("sending chksum 0x%x\n", repl_icmp_hdr->ih_chksum));
578 repl_ip_hdr->acc_next= repl_icmp;
579 repl_icmp->acc_next= bf_cut (icmp_data, ICMP_MIN_HDR_SIZE,
580 icmp_len - ICMP_MIN_HDR_SIZE);
582 bf_afree(ip_data);
583 bf_afree(icmp_data);
585 enqueue_pack(icmp_port, repl_ip_hdr);
588 PRIVATE u16_t icmp_pack_oneCsum(icmp_pack)
589 acc_t *icmp_pack;
591 u16_t prev;
592 int odd_byte;
593 char *data_ptr;
594 int length;
595 char byte_buf[2];
597 prev= 0;
599 odd_byte= FALSE;
600 for (; icmp_pack; icmp_pack= icmp_pack->acc_next)
602 data_ptr= ptr2acc_data(icmp_pack);
603 length= icmp_pack->acc_length;
605 if (!length)
606 continue;
607 if (odd_byte)
609 byte_buf[1]= *data_ptr;
610 prev= oneC_sum(prev, (u16_t *)byte_buf, 2);
611 data_ptr++;
612 length--;
613 odd_byte= FALSE;
615 if (length & 1)
617 odd_byte= TRUE;
618 length--;
619 byte_buf[0]= data_ptr[length];
621 if (!length)
622 continue;
623 prev= oneC_sum (prev, (u16_t *)data_ptr, length);
625 if (odd_byte)
626 prev= oneC_sum (prev, (u16_t *)byte_buf, 1);
627 return prev;
630 PRIVATE acc_t *make_repl_ip(ip_hdr, ip_len)
631 ip_hdr_t *ip_hdr;
632 int ip_len;
634 ip_hdr_t *repl_ip_hdr;
635 acc_t *repl;
636 int repl_hdr_len;
638 if (ip_len>IP_MIN_HDR_SIZE)
640 DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n"));
641 ip_len= IP_MIN_HDR_SIZE;
644 repl_hdr_len= IP_MIN_HDR_SIZE;
646 repl= bf_memreq(repl_hdr_len);
648 repl_ip_hdr= (ip_hdr_t *)ptr2acc_data(repl);
650 repl_ip_hdr->ih_vers_ihl= repl_hdr_len >> 2;
651 repl_ip_hdr->ih_tos= ip_hdr->ih_tos;
652 repl_ip_hdr->ih_ttl= ICMP_DEF_TTL;
653 repl_ip_hdr->ih_proto= IPPROTO_ICMP;
654 repl_ip_hdr->ih_dst= ip_hdr->ih_src;
655 repl_ip_hdr->ih_flags_fragoff= 0;
657 return repl;
660 PRIVATE void enqueue_pack(icmp_port, reply_ip_hdr)
661 icmp_port_t *icmp_port;
662 acc_t *reply_ip_hdr;
664 int r;
665 ev_arg_t ev_arg;
667 /* Check rate */
668 if (icmp_port->icp_rate_count >= ICMP_MAX_RATE)
670 /* Something is going wrong; check policy */
671 r= icmp_rate_limit(icmp_port, reply_ip_hdr);
672 if (r == -1)
674 bf_afree(reply_ip_hdr);
675 reply_ip_hdr= NULL;
676 return;
679 /* OK, continue */
681 icmp_port->icp_rate_count++;
683 reply_ip_hdr->acc_ext_link= 0;
685 if (icmp_port->icp_head_queue)
687 icmp_port->icp_tail_queue->acc_ext_link=
688 reply_ip_hdr;
690 else
692 icmp_port->icp_head_queue= reply_ip_hdr;
694 reply_ip_hdr->acc_ext_link= NULL;
695 icmp_port->icp_tail_queue= reply_ip_hdr;
697 if (!(icmp_port->icp_flags & ICPF_WRITE_IP))
699 icmp_port->icp_flags |= ICPF_WRITE_IP;
700 ev_arg.ev_ptr= icmp_port;
701 ev_enqueue(&icmp_port->icp_event, icmp_write, ev_arg);
705 PRIVATE int icmp_rate_limit(icmp_port, reply_ip_hdr)
706 icmp_port_t *icmp_port;
707 acc_t *reply_ip_hdr;
709 time_t t;
710 acc_t *pack;
711 ip_hdr_t *ip_hdr;
712 icmp_hdr_t *icmp_hdr;
713 int hdrlen, icmp_hdr_len, type;
715 /* Check the time first */
716 t= get_time();
717 if (t >= icmp_port->icp_rate_lasttime + ICMP_RATE_INTERVAL)
719 icmp_port->icp_rate_lasttime= t;
720 icmp_port->icp_rate_count= 0;
721 return 0;
724 icmp_port->icp_rate_count++;
726 /* Adjust report limit if necessary */
727 if (icmp_port->icp_rate_count >
728 icmp_port->icp_rate_report+ICMP_RATE_WARN)
730 icmp_port->icp_rate_report *= 2;
731 return -1;
734 /* Do we need to report */
735 if (icmp_port->icp_rate_count < icmp_port->icp_rate_report)
736 return -1;
738 pack= bf_dupacc(reply_ip_hdr);
739 pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE);
740 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
741 printf("icmp[%d]: dropping ICMP packet #%d to ",
742 icmp_port->icp_ipport, icmp_port->icp_rate_count);
743 writeIpAddr(ip_hdr->ih_dst);
744 hdrlen= (ip_hdr->ih_vers_ihl & IH_IHL_MASK)*4;
745 pack= bf_packIffLess(pack, hdrlen+ICMP_MIN_HDR_SIZE);
746 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
747 icmp_hdr= (icmp_hdr_t *)(ptr2acc_data(pack)+hdrlen);
748 type= icmp_hdr->ih_type;
749 printf(" type %d, code %d\n", type, icmp_hdr->ih_code);
750 switch(type)
752 case ICMP_TYPE_DST_UNRCH:
753 case ICMP_TYPE_SRC_QUENCH:
754 case ICMP_TYPE_REDIRECT:
755 case ICMP_TYPE_TIME_EXCEEDED:
756 case ICMP_TYPE_PARAM_PROBLEM:
757 icmp_hdr_len= offsetof(struct icmp_hdr, ih_dun);
758 pack= bf_packIffLess(pack,
759 hdrlen+icmp_hdr_len+IP_MIN_HDR_SIZE);
760 ip_hdr= (ip_hdr_t *)(ptr2acc_data(pack)+hdrlen+icmp_hdr_len);
761 icmp_hdr= (icmp_hdr_t *)(ptr2acc_data(pack)+hdrlen);
762 printf("\tinfo %08x, original dst ",
763 ntohs(icmp_hdr->ih_hun.ihh_unused));
764 writeIpAddr(ip_hdr->ih_dst);
765 printf(", proto %d, length %u\n",
766 ip_hdr->ih_proto, ntohs(ip_hdr->ih_length));
767 break;
768 default:
769 break;
771 bf_afree(pack); pack= NULL;
773 return -1;
776 PRIVATE void icmp_write(ev, ev_arg)
777 event_t *ev;
778 ev_arg_t ev_arg;
780 int result;
781 icmp_port_t *icmp_port;
782 acc_t *data;
784 icmp_port= ev_arg.ev_ptr;
785 assert(ev == &icmp_port->icp_event);
787 assert (icmp_port->icp_flags & ICPF_WRITE_IP);
788 assert (!(icmp_port->icp_flags & ICPF_WRITE_SP));
790 while (icmp_port->icp_head_queue != NULL)
792 data= icmp_port->icp_head_queue;
793 icmp_port->icp_head_queue= data->acc_ext_link;
795 result= ip_send(icmp_port->icp_ipfd, data,
796 bf_bufsize(data));
797 if (result != NW_WOULDBLOCK)
799 if (result == NW_OK)
800 continue;
801 DBLOCK(1, printf("icmp_write: error %d\n", result););
802 continue;
805 assert(icmp_port->icp_write_pack == NULL);
806 icmp_port->icp_write_pack= data;
808 result= ip_write(icmp_port->icp_ipfd,
809 bf_bufsize(icmp_port->icp_write_pack));
810 if (result == NW_SUSPEND)
812 icmp_port->icp_flags |= ICPF_WRITE_SP;
813 return;
816 icmp_port->icp_flags &= ~ICPF_WRITE_IP;
819 PRIVATE void icmp_buffree(priority)
820 int priority;
822 acc_t *tmp_acc;
823 int i;
824 icmp_port_t *icmp_port;
826 if (priority == ICMP_PRI_QUEUE)
828 for (i=0, icmp_port= icmp_port_table; i<ip_conf_nr;
829 i++, icmp_port++)
831 while(icmp_port->icp_head_queue)
833 tmp_acc= icmp_port->icp_head_queue;
834 icmp_port->icp_head_queue=
835 tmp_acc->acc_ext_link;
836 bf_afree(tmp_acc);
842 #ifdef BUF_CONSISTENCY_CHECK
843 PRIVATE void icmp_bufcheck()
845 int i;
846 icmp_port_t *icmp_port;
847 acc_t *pack;
849 for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++)
851 for (pack= icmp_port->icp_head_queue; pack;
852 pack= pack->acc_ext_link)
854 bf_check_acc(pack);
856 bf_check_acc(icmp_port->icp_write_pack);
859 #endif
861 PRIVATE void icmp_dst_unreach(icmp_port, ip_pack, ip_hdr_len, ip_hdr, icmp_pack,
862 icmp_len, icmp_hdr)
863 icmp_port_t *icmp_port;
864 acc_t *ip_pack;
865 int ip_hdr_len;
866 ip_hdr_t *ip_hdr;
867 acc_t *icmp_pack;
868 int icmp_len;
869 icmp_hdr_t *icmp_hdr;
871 acc_t *old_ip_pack;
872 ip_hdr_t *old_ip_hdr;
873 int ip_port_nr;
874 ipaddr_t dst, mask;
875 size_t old_pack_size;
876 u16_t new_mtu;
878 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
880 DBLOCK(1, printf("dest unrch with wrong size\n"));
881 return;
883 old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8);
884 old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE);
885 old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack);
887 if (old_ip_hdr->ih_src != ip_hdr->ih_dst)
889 DBLOCK(1, printf("dest unrch based on wrong packet\n"));
890 bf_afree(old_ip_pack);
891 return;
894 ip_port_nr= icmp_port->icp_ipport;
896 switch(icmp_hdr->ih_code)
898 case ICMP_NET_UNRCH:
899 dst= old_ip_hdr->ih_dst;
900 mask= ip_get_netmask(dst);
901 ipr_destunrch (ip_port_nr, dst & mask, mask,
902 IPR_UNRCH_TIMEOUT);
903 break;
904 case ICMP_HOST_UNRCH:
905 ipr_destunrch (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1,
906 IPR_UNRCH_TIMEOUT);
907 break;
908 case ICMP_PORT_UNRCH:
909 /* At the moment we don't do anything with this information.
910 * It should be handed to the appropriate transport layer.
912 break;
913 case ICMP_FRAGM_AND_DF:
915 DBLOCK(1, printf("icmp_dst_unreach: got mtu icmp from ");
916 writeIpAddr(ip_hdr->ih_src);
917 printf("; original destination: ");
918 writeIpAddr(old_ip_hdr->ih_dst);
919 printf("; protocol: %d\n",
920 old_ip_hdr->ih_proto));
921 old_pack_size= ntohs(old_ip_hdr->ih_length);
922 if (!old_pack_size)
923 break;
924 new_mtu= ntohs(icmp_hdr->ih_hun.ihh_mtu.im_mtu);
925 if (!new_mtu || new_mtu > old_pack_size)
926 new_mtu= old_pack_size-1;
927 ipr_mtu(ip_port_nr, old_ip_hdr->ih_dst, new_mtu,
928 IPR_MTU_TIMEOUT);
929 break;
931 default:
932 DBLOCK(1, printf("icmp_dst_unreach: got strange code %d from ",
933 icmp_hdr->ih_code);
934 writeIpAddr(ip_hdr->ih_src);
935 printf("; original destination: ");
936 writeIpAddr(old_ip_hdr->ih_dst);
937 printf("; protocol: %d\n",
938 old_ip_hdr->ih_proto));
939 break;
941 bf_afree(old_ip_pack);
944 PRIVATE void icmp_time_exceeded(icmp_port, ip_pack, ip_hdr_len, ip_hdr,
945 icmp_pack, icmp_len, icmp_hdr)
946 icmp_port_t *icmp_port;
947 acc_t *ip_pack;
948 int ip_hdr_len;
949 ip_hdr_t *ip_hdr;
950 acc_t *icmp_pack;
951 int icmp_len;
952 icmp_hdr_t *icmp_hdr;
954 acc_t *old_ip_pack;
955 ip_hdr_t *old_ip_hdr;
956 int ip_port_nr;
958 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
960 DBLOCK(1, printf("time exceeded with wrong size\n"));
961 return;
963 old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8);
964 old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE);
965 old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack);
967 if (old_ip_hdr->ih_src != ip_hdr->ih_dst)
969 DBLOCK(1, printf("time exceeded based on wrong packet\n"));
970 bf_afree(old_ip_pack);
971 return;
974 ip_port_nr= icmp_port->icp_ipport;
976 switch(icmp_hdr->ih_code)
978 case ICMP_TTL_EXC:
979 ipr_ttl_exc (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1,
980 IPR_TTL_TIMEOUT);
981 break;
982 case ICMP_FRAG_REASSEM:
983 /* Ignore reassembly time-outs. */
984 break;
985 default:
986 DBLOCK(1, printf("got strange code: %d\n",
987 icmp_hdr->ih_code));
988 break;
990 bf_afree(old_ip_pack);
993 PRIVATE void icmp_router_advertisement(icmp_port, icmp_pack, icmp_len, icmp_hdr)
994 icmp_port_t *icmp_port;
995 acc_t *icmp_pack;
996 int icmp_len;
997 icmp_hdr_t *icmp_hdr;
999 int entries;
1000 int entry_size;
1001 u32_t addr;
1002 i32_t pref;
1003 u16_t lifetime;
1004 int i;
1005 char *bufp;
1006 ip_port_t *ip_port;
1008 if (icmp_len < 8)
1010 DBLOCK(1,
1011 printf("router advertisement with wrong size (%d)\n",
1012 icmp_len));
1013 return;
1015 if (icmp_hdr->ih_code != 0)
1017 DBLOCK(1,
1018 printf("router advertisement with wrong code (%d)\n",
1019 icmp_hdr->ih_code));
1020 return;
1022 entries= icmp_hdr->ih_hun.ihh_ram.iram_na;
1023 entry_size= icmp_hdr->ih_hun.ihh_ram.iram_aes * 4;
1024 if (entries < 1)
1026 DBLOCK(1, printf(
1027 "router advertisement with wrong number of entries (%d)\n",
1028 entries));
1029 return;
1031 if (entry_size < 8)
1033 DBLOCK(1, printf(
1034 "router advertisement with wrong entry size (%d)\n",
1035 entry_size));
1036 return;
1038 if (icmp_len < 8 + entries * entry_size)
1040 DBLOCK(1,
1041 printf("router advertisement with wrong size\n");
1042 printf(
1043 "\t(entries= %d, entry_size= %d, icmp_len= %d)\n",
1044 entries, entry_size, icmp_len));
1045 return;
1047 lifetime= ntohs(icmp_hdr->ih_hun.ihh_ram.iram_lt);
1048 if (lifetime > 9000)
1050 DBLOCK(1, printf(
1051 "router advertisement with wrong lifetime (%d)\n",
1052 lifetime));
1053 return;
1055 ip_port= &ip_port_table[icmp_port->icp_ipport];
1056 for (i= 0, bufp= (char *)&icmp_hdr->ih_dun.uhd_data[0]; i< entries; i++,
1057 bufp += entry_size)
1059 addr= *(ipaddr_t *)bufp;
1060 pref= ntohl(*(u32_t *)(bufp+4));
1061 ipr_add_oroute(icmp_port->icp_ipport, HTONL(0L), HTONL(0L),
1062 addr, lifetime ? lifetime * HZ : 1,
1063 1, 0, 0, pref, NULL);
1067 PRIVATE void icmp_redirect(icmp_port, ip_hdr, icmp_pack, icmp_len, icmp_hdr)
1068 icmp_port_t *icmp_port;
1069 ip_hdr_t *ip_hdr;
1070 acc_t *icmp_pack;
1071 int icmp_len;
1072 icmp_hdr_t *icmp_hdr;
1074 acc_t *old_ip_pack;
1075 ip_hdr_t *old_ip_hdr;
1076 int ip_port_nr;
1077 ipaddr_t dst, mask;
1079 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
1081 DBLOCK(1, printf("redirect with wrong size\n"));
1082 return;
1084 old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8);
1085 old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE);
1086 old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack);
1088 ip_port_nr= icmp_port->icp_ipport;
1090 switch(icmp_hdr->ih_code)
1092 case ICMP_REDIRECT_NET:
1093 dst= old_ip_hdr->ih_dst;
1094 mask= ip_get_netmask(dst);
1095 ipr_redirect (ip_port_nr, dst & mask, mask,
1096 ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway,
1097 IPR_REDIRECT_TIMEOUT);
1098 break;
1099 case ICMP_REDIRECT_HOST:
1100 ipr_redirect (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1,
1101 ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway,
1102 IPR_REDIRECT_TIMEOUT);
1103 break;
1104 default:
1105 DBLOCK(1, printf("got strange code: %d\n",
1106 icmp_hdr->ih_code));
1107 break;
1109 bf_afree(old_ip_pack);
1112 PRIVATE acc_t *icmp_err_pack(pack, icmp_hdr_pp)
1113 acc_t *pack;
1114 icmp_hdr_t **icmp_hdr_pp;
1116 ip_hdr_t *ip_hdr;
1117 icmp_hdr_t *icmp_hdr_p;
1118 acc_t *ip_pack, *icmp_pack, *tmp_pack;
1119 int ip_hdr_len, icmp_hdr_len, ih_type;
1120 size_t size, pack_len;
1121 ipaddr_t dest, netmask;
1122 nettype_t nettype;
1124 pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE);
1125 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
1126 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
1127 pack_len= bf_bufsize(pack);
1129 /* If the IP protocol is ICMP (except echo request/reply) or the
1130 * fragment offset is non-zero,
1131 * drop the packet. Also check if the source address is valid.
1133 if ((ntohs(ip_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) != 0)
1135 bf_afree(pack);
1136 return NULL;
1138 if (ip_hdr->ih_proto == IPPROTO_ICMP)
1140 if (ip_hdr_len>IP_MIN_HDR_SIZE)
1142 pack= bf_packIffLess(pack, ip_hdr_len);
1143 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
1146 if (pack_len < ip_hdr_len+ICMP_MIN_HDR_SIZE)
1148 bf_afree(pack);
1149 return NULL;
1151 icmp_pack= bf_cut(pack, ip_hdr_len, ICMP_MIN_HDR_SIZE);
1152 icmp_pack= bf_packIffLess (icmp_pack, ICMP_MIN_HDR_SIZE);
1153 icmp_hdr_p= (icmp_hdr_t *)ptr2acc_data(icmp_pack);
1154 ih_type= icmp_hdr_p->ih_type;
1155 bf_afree(icmp_pack); icmp_pack= NULL;
1157 if (ih_type != ICMP_TYPE_ECHO_REQ &&
1158 ih_type != ICMP_TYPE_ECHO_REPL)
1160 bf_afree(pack);
1161 return NULL;
1164 dest= ip_hdr->ih_src;
1165 nettype= ip_nettype(dest);
1166 netmask= ip_netmask(nettype);
1167 if (nettype != IPNT_CLASS_A && nettype != IPNT_LOCAL &&
1168 nettype != IPNT_CLASS_B && nettype != IPNT_CLASS_C)
1170 printf("icmp_err_pack: invalid source address: ");
1171 writeIpAddr(dest);
1172 printf("\n");
1173 bf_afree(pack);
1174 return NULL;
1177 /* Take the IP header and the first 64 bits of user data. */
1178 size= ntohs(ip_hdr->ih_length);
1179 if (size < ip_hdr_len || pack_len < size)
1181 printf("icmp_err_pack: wrong packet size:\n");
1182 printf("\thdrlen= %d, ih_length= %d, bufsize= %d\n",
1183 ip_hdr_len, size, pack_len);
1184 bf_afree(pack);
1185 return NULL;
1187 if (ip_hdr_len + 8 < size)
1188 size= ip_hdr_len+8;
1189 tmp_pack= bf_cut(pack, 0, size);
1190 bf_afree(pack);
1191 pack= tmp_pack;
1192 tmp_pack= NULL;
1194 /* Create a minimal size ICMP hdr. */
1195 icmp_hdr_len= offsetof(icmp_hdr_t, ih_dun);
1196 icmp_pack= bf_memreq(icmp_hdr_len);
1197 pack= bf_append(icmp_pack, pack);
1198 size += icmp_hdr_len;
1199 pack= bf_packIffLess(pack, icmp_hdr_len);
1200 icmp_hdr_p= (icmp_hdr_t *)ptr2acc_data(pack);
1201 icmp_hdr_p->ih_type= 0;
1202 icmp_hdr_p->ih_code= 0;
1203 icmp_hdr_p->ih_chksum= 0;
1204 icmp_hdr_p->ih_hun.ihh_unused= 0;
1205 icmp_hdr_p->ih_chksum= ~icmp_pack_oneCsum(pack);
1206 *icmp_hdr_pp= icmp_hdr_p;
1208 /* Create an IP header */
1209 ip_hdr_len= IP_MIN_HDR_SIZE;
1211 ip_pack= bf_memreq(ip_hdr_len);
1212 ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack);
1214 ip_hdr->ih_vers_ihl= ip_hdr_len >> 2;
1215 ip_hdr->ih_tos= 0;
1216 ip_hdr->ih_length= htons(ip_hdr_len + size);
1217 ip_hdr->ih_flags_fragoff= 0;
1218 ip_hdr->ih_ttl= ICMP_DEF_TTL;
1219 ip_hdr->ih_proto= IPPROTO_ICMP;
1220 ip_hdr->ih_dst= dest;
1222 assert(ip_pack->acc_next == NULL);
1223 ip_pack->acc_next= pack;
1224 return ip_pack;
1228 * $PchId: icmp.c,v 1.23 2005/06/28 14:16:56 philip Exp $