iso9660fs: initialize buffer cache
[minix.git] / servers / inet / generic / icmp.c
blobedb97a39add174144fb2a7e676cb407d313f48f3
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 static icmp_port_t *icmp_port_table;
52 static void icmp_main ARGS(( icmp_port_t *icmp_port ));
53 static acc_t *icmp_getdata ARGS(( int port, size_t offset,
54 size_t count, int for_ioctl ));
55 static int icmp_putdata ARGS(( int port, size_t offset,
56 acc_t *data, int for_ioctl ));
57 static void icmp_read ARGS(( icmp_port_t *icmp_port ));
58 static void process_data ARGS(( icmp_port_t *icmp_port,
59 acc_t *data ));
60 static u16_t icmp_pack_oneCsum ARGS(( acc_t *ip_pack ));
61 static 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 static 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 static 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 static void icmp_router_advertisement ARGS(( icmp_port_t *icmp_port,
71 acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr ));
72 static 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 static acc_t *make_repl_ip ARGS(( ip_hdr_t *ip_hdr,
76 int ip_len ));
77 static void enqueue_pack ARGS(( icmp_port_t *icmp_port,
78 acc_t *reply_ip_hdr ));
79 static int icmp_rate_limit ARGS(( icmp_port_t *icmp_port,
80 acc_t *reply_ip_hdr ));
81 static void icmp_write ARGS(( event_t *ev, ev_arg_t ev_arg ));
82 static void icmp_buffree ARGS(( int priority ));
83 static acc_t *icmp_err_pack ARGS(( acc_t *pack, icmp_hdr_t **icmp_hdr_pp ));
84 #ifdef BUF_CONSISTENCY_CHECK
85 static void icmp_bufcheck ARGS(( void ));
86 #endif
88 void icmp_prep()
90 icmp_port_table= alloc(ip_conf_nr * sizeof(icmp_port_table[0]));
93 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 static 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 static 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 static 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 static 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 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 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 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 void icmp_snd_mtu(
375 int port_nr,
376 acc_t *pack,
377 u16_t mtu
380 icmp_hdr_t *icmp_hdr;
381 icmp_port_t *icmp_port;
383 if (port_nr >= 0 && port_nr < ip_conf_nr)
384 icmp_port= &icmp_port_table[port_nr];
385 else
387 printf("icmp_snd_mtu: strange port %d\n", port_nr);
388 bf_afree(pack);
389 return;
392 pack= icmp_err_pack(pack, &icmp_hdr);
393 if (pack == NULL)
394 return;
395 icmp_hdr->ih_type= ICMP_TYPE_DST_UNRCH;
396 icmp_hdr->ih_code= ICMP_FRAGM_AND_DF;
397 icmp_hdr->ih_hun.ihh_mtu.im_mtu= htons(mtu);
398 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
399 (u16_t *)&icmp_hdr->ih_type, 2);
400 icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum,
401 (u16_t *)&icmp_hdr->ih_hun.ihh_mtu.im_mtu, 2);
402 enqueue_pack(icmp_port, pack);
405 static void process_data(icmp_port, data)
406 icmp_port_t *icmp_port;
407 acc_t *data;
409 ip_hdr_t *ip_hdr;
410 icmp_hdr_t *icmp_hdr;
411 acc_t *icmp_data;
412 int ip_hdr_len;
413 size_t pack_len;
415 /* Align entire packet */
416 data= bf_align(data, BUF_S, 4);
418 data= bf_packIffLess(data, IP_MIN_HDR_SIZE);
419 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
420 DIFBLOCK(0x10, (ip_hdr->ih_dst & HTONL(0xf0000000)) == HTONL(0xe0000000),
421 printf("got multicast packet\n"));
422 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
424 if (ip_hdr_len>IP_MIN_HDR_SIZE)
426 data= bf_packIffLess(data, ip_hdr_len);
427 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
430 pack_len= bf_bufsize(data);
431 pack_len -= ip_hdr_len;
432 if (pack_len < ICMP_MIN_HDR_SIZE)
434 if (pack_len == 0 && ip_hdr->ih_proto == 0)
436 /* IP layer reports new ip address, which can be
437 * ignored.
440 else
441 DBLOCK(1, printf("got an incomplete icmp packet\n"));
442 bf_afree(data);
443 return;
446 icmp_data= bf_cut(data, ip_hdr_len, pack_len);
448 icmp_data= bf_packIffLess (icmp_data, ICMP_MIN_HDR_SIZE);
449 icmp_hdr= (icmp_hdr_t *)ptr2acc_data(icmp_data);
451 if ((u16_t)~icmp_pack_oneCsum(icmp_data))
453 DBLOCK(1, printf(
454 "got packet with bad checksum (= 0x%x, 0x%x)\n",
455 icmp_hdr->ih_chksum,
456 (u16_t)~icmp_pack_oneCsum(icmp_data)));
457 bf_afree(data);
458 bf_afree(icmp_data);
459 return;
462 switch (icmp_hdr->ih_type)
464 case ICMP_TYPE_ECHO_REPL:
465 break;
466 case ICMP_TYPE_DST_UNRCH:
467 icmp_dst_unreach (icmp_port, data, ip_hdr_len, ip_hdr,
468 icmp_data, pack_len, icmp_hdr);
469 break;
470 case ICMP_TYPE_SRC_QUENCH:
471 /* Ignore src quench ICMPs */
472 DBLOCK(2, printf("ignoring SRC QUENCH ICMP.\n"));
473 break;
474 case ICMP_TYPE_REDIRECT:
475 icmp_redirect (icmp_port, ip_hdr, icmp_data, pack_len,
476 icmp_hdr);
477 break;
478 case ICMP_TYPE_ECHO_REQ:
479 icmp_echo_request(icmp_port, data, ip_hdr_len, ip_hdr,
480 icmp_data, pack_len, icmp_hdr);
481 return;
482 case ICMP_TYPE_ROUTER_ADVER:
483 icmp_router_advertisement(icmp_port, icmp_data, pack_len,
484 icmp_hdr);
485 break;
486 case ICMP_TYPE_ROUTE_SOL:
487 break; /* Should be handled by a routing deamon. */
488 case ICMP_TYPE_TIME_EXCEEDED:
489 icmp_time_exceeded (icmp_port, data, ip_hdr_len, ip_hdr,
490 icmp_data, pack_len, icmp_hdr);
491 break;
492 default:
493 DBLOCK(1, printf("got an unknown icmp (%d) from ",
494 icmp_hdr->ih_type);
495 writeIpAddr(ip_hdr->ih_src); printf("\n"));
496 break;
498 bf_afree(data);
499 bf_afree(icmp_data);
502 static void icmp_echo_request(icmp_port, ip_data, ip_len, ip_hdr,
503 icmp_data, icmp_len, icmp_hdr)
504 icmp_port_t *icmp_port;
505 acc_t *ip_data, *icmp_data;
506 int ip_len, icmp_len;
507 ip_hdr_t *ip_hdr;
508 icmp_hdr_t *icmp_hdr;
510 acc_t *repl_ip_hdr, *repl_icmp;
511 ipaddr_t tmpaddr, locaddr, netmask;
512 icmp_hdr_t *repl_icmp_hdr;
513 i32_t tmp_chksum;
514 ip_port_t *ip_port;
516 if (icmp_hdr->ih_code != 0)
518 DBLOCK(1,
519 printf("got an icmp echo request with unknown code (%d)\n",
520 icmp_hdr->ih_code));
521 bf_afree(ip_data);
522 bf_afree(icmp_data);
523 return;
525 if (icmp_len < ICMP_MIN_HDR_SIZE + sizeof(icmp_id_seq_t))
527 DBLOCK(1, printf("got an incomplete icmp echo request\n"));
528 bf_afree(ip_data);
529 bf_afree(icmp_data);
530 return;
532 tmpaddr= ntohl(ip_hdr->ih_dst);
533 if ((tmpaddr & 0xe0000000) == 0xe0000000 &&
534 tmpaddr != 0xffffffff)
536 /* Respond only to the all hosts multicast address until
537 * a decent listening service has been implemented
539 if (tmpaddr != 0xe0000001)
541 bf_afree(ip_data);
542 bf_afree(icmp_data);
543 return;
547 /* Limit subnet broadcasts to the local net */
548 ip_port= &ip_port_table[icmp_port->icp_ipport];
549 locaddr= ip_port->ip_ipaddr;
550 netmask= ip_port->ip_subnetmask;
551 if (ip_hdr->ih_dst == (locaddr | ~netmask) &&
552 (ip_port->ip_flags & IPF_SUBNET_BCAST) &&
553 ((ip_hdr->ih_src ^ locaddr) & netmask) != 0)
555 /* Directed broadcast */
556 bf_afree(ip_data);
557 bf_afree(icmp_data);
558 return;
561 repl_ip_hdr= make_repl_ip(ip_hdr, ip_len);
562 repl_icmp= bf_memreq (ICMP_MIN_HDR_SIZE);
563 repl_icmp_hdr= (icmp_hdr_t *)ptr2acc_data(repl_icmp);
564 repl_icmp_hdr->ih_type= ICMP_TYPE_ECHO_REPL;
565 repl_icmp_hdr->ih_code= 0;
567 DBLOCK(2,
568 printf("ih_chksum= 0x%x, ih_type= 0x%x, repl->ih_type= 0x%x\n",
569 icmp_hdr->ih_chksum, *(u16_t *)&icmp_hdr->ih_type,
570 *(u16_t *)&repl_icmp_hdr->ih_type));
571 tmp_chksum= (~icmp_hdr->ih_chksum & 0xffff) -
572 (i32_t)*(u16_t *)&icmp_hdr->ih_type+
573 *(u16_t *)&repl_icmp_hdr->ih_type;
574 tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff);
575 tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff);
576 repl_icmp_hdr->ih_chksum= ~tmp_chksum;
577 DBLOCK(2, printf("sending chksum 0x%x\n", repl_icmp_hdr->ih_chksum));
579 repl_ip_hdr->acc_next= repl_icmp;
580 repl_icmp->acc_next= bf_cut (icmp_data, ICMP_MIN_HDR_SIZE,
581 icmp_len - ICMP_MIN_HDR_SIZE);
583 bf_afree(ip_data);
584 bf_afree(icmp_data);
586 enqueue_pack(icmp_port, repl_ip_hdr);
589 static u16_t icmp_pack_oneCsum(icmp_pack)
590 acc_t *icmp_pack;
592 u16_t prev;
593 int odd_byte;
594 char *data_ptr;
595 int length;
596 char byte_buf[2];
598 prev= 0;
600 odd_byte= FALSE;
601 for (; icmp_pack; icmp_pack= icmp_pack->acc_next)
603 data_ptr= ptr2acc_data(icmp_pack);
604 length= icmp_pack->acc_length;
606 if (!length)
607 continue;
608 if (odd_byte)
610 byte_buf[1]= *data_ptr;
611 prev= oneC_sum(prev, (u16_t *)byte_buf, 2);
612 data_ptr++;
613 length--;
614 odd_byte= FALSE;
616 if (length & 1)
618 odd_byte= TRUE;
619 length--;
620 byte_buf[0]= data_ptr[length];
622 if (!length)
623 continue;
624 prev= oneC_sum (prev, (u16_t *)data_ptr, length);
626 if (odd_byte)
627 prev= oneC_sum (prev, (u16_t *)byte_buf, 1);
628 return prev;
631 static acc_t *make_repl_ip(ip_hdr, ip_len)
632 ip_hdr_t *ip_hdr;
633 int ip_len;
635 ip_hdr_t *repl_ip_hdr;
636 acc_t *repl;
637 int repl_hdr_len;
639 if (ip_len>IP_MIN_HDR_SIZE)
641 DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n"));
642 ip_len= IP_MIN_HDR_SIZE;
645 repl_hdr_len= IP_MIN_HDR_SIZE;
647 repl= bf_memreq(repl_hdr_len);
649 repl_ip_hdr= (ip_hdr_t *)ptr2acc_data(repl);
651 repl_ip_hdr->ih_vers_ihl= repl_hdr_len >> 2;
652 repl_ip_hdr->ih_tos= ip_hdr->ih_tos;
653 repl_ip_hdr->ih_ttl= ICMP_DEF_TTL;
654 repl_ip_hdr->ih_proto= IPPROTO_ICMP;
655 repl_ip_hdr->ih_dst= ip_hdr->ih_src;
656 repl_ip_hdr->ih_flags_fragoff= 0;
658 return repl;
661 static void enqueue_pack(icmp_port, reply_ip_hdr)
662 icmp_port_t *icmp_port;
663 acc_t *reply_ip_hdr;
665 int r;
666 ev_arg_t ev_arg;
668 /* Check rate */
669 if (icmp_port->icp_rate_count >= ICMP_MAX_RATE)
671 /* Something is going wrong; check policy */
672 r= icmp_rate_limit(icmp_port, reply_ip_hdr);
673 if (r == -1)
675 bf_afree(reply_ip_hdr);
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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 u16_t lifetime;
1002 int i;
1003 char *bufp;
1005 if (icmp_len < 8)
1007 DBLOCK(1,
1008 printf("router advertisement with wrong size (%d)\n",
1009 icmp_len));
1010 return;
1012 if (icmp_hdr->ih_code != 0)
1014 DBLOCK(1,
1015 printf("router advertisement with wrong code (%d)\n",
1016 icmp_hdr->ih_code));
1017 return;
1019 entries= icmp_hdr->ih_hun.ihh_ram.iram_na;
1020 entry_size= icmp_hdr->ih_hun.ihh_ram.iram_aes * 4;
1021 if (entries < 1)
1023 DBLOCK(1, printf(
1024 "router advertisement with wrong number of entries (%d)\n",
1025 entries));
1026 return;
1028 if (entry_size < 8)
1030 DBLOCK(1, printf(
1031 "router advertisement with wrong entry size (%d)\n",
1032 entry_size));
1033 return;
1035 if (icmp_len < 8 + entries * entry_size)
1037 DBLOCK(1,
1038 printf("router advertisement with wrong size\n");
1039 printf(
1040 "\t(entries= %d, entry_size= %d, icmp_len= %d)\n",
1041 entries, entry_size, icmp_len));
1042 return;
1044 lifetime= ntohs(icmp_hdr->ih_hun.ihh_ram.iram_lt);
1045 if (lifetime > 9000)
1047 DBLOCK(1, printf(
1048 "router advertisement with wrong lifetime (%d)\n",
1049 lifetime));
1050 return;
1052 for (i= 0, bufp= (char *)&icmp_hdr->ih_dun.uhd_data[0]; i< entries; i++,
1053 bufp += entry_size)
1055 u32_t addr;
1056 i32_t pref;
1058 addr= *(ipaddr_t *)bufp;
1059 pref= ntohl(*(u32_t *)(bufp+4));
1060 ipr_add_oroute(icmp_port->icp_ipport, HTONL(0L), HTONL(0L),
1061 addr, lifetime ? lifetime * HZ : 1,
1062 1, 0, 0, pref, NULL);
1066 static void icmp_redirect(icmp_port, ip_hdr, icmp_pack, icmp_len, icmp_hdr)
1067 icmp_port_t *icmp_port;
1068 ip_hdr_t *ip_hdr;
1069 acc_t *icmp_pack;
1070 int icmp_len;
1071 icmp_hdr_t *icmp_hdr;
1073 acc_t *old_ip_pack;
1074 ip_hdr_t *old_ip_hdr;
1075 int ip_port_nr;
1076 ipaddr_t dst, mask;
1078 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
1080 DBLOCK(1, printf("redirect with wrong size\n"));
1081 return;
1083 old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8);
1084 old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE);
1085 old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack);
1087 ip_port_nr= icmp_port->icp_ipport;
1089 switch(icmp_hdr->ih_code)
1091 case ICMP_REDIRECT_NET:
1092 dst= old_ip_hdr->ih_dst;
1093 mask= ip_get_netmask(dst);
1094 ipr_redirect (ip_port_nr, dst & mask, mask,
1095 ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway,
1096 IPR_REDIRECT_TIMEOUT);
1097 break;
1098 case ICMP_REDIRECT_HOST:
1099 ipr_redirect (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1,
1100 ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway,
1101 IPR_REDIRECT_TIMEOUT);
1102 break;
1103 default:
1104 DBLOCK(1, printf("got strange code: %d\n",
1105 icmp_hdr->ih_code));
1106 break;
1108 bf_afree(old_ip_pack);
1111 static acc_t *icmp_err_pack(pack, icmp_hdr_pp)
1112 acc_t *pack;
1113 icmp_hdr_t **icmp_hdr_pp;
1115 ip_hdr_t *ip_hdr;
1116 icmp_hdr_t *icmp_hdr_p;
1117 acc_t *ip_pack, *icmp_pack, *tmp_pack;
1118 int ip_hdr_len, icmp_hdr_len, ih_type;
1119 size_t size, pack_len;
1120 ipaddr_t dest;
1121 nettype_t nettype;
1123 pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE);
1124 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
1125 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
1126 pack_len= bf_bufsize(pack);
1128 /* If the IP protocol is ICMP (except echo request/reply) or the
1129 * fragment offset is non-zero,
1130 * drop the packet. Also check if the source address is valid.
1132 if ((ntohs(ip_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) != 0)
1134 bf_afree(pack);
1135 return NULL;
1137 if (ip_hdr->ih_proto == IPPROTO_ICMP)
1139 if (ip_hdr_len>IP_MIN_HDR_SIZE)
1141 pack= bf_packIffLess(pack, ip_hdr_len);
1142 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
1145 if (pack_len < ip_hdr_len+ICMP_MIN_HDR_SIZE)
1147 bf_afree(pack);
1148 return NULL;
1150 icmp_pack= bf_cut(pack, ip_hdr_len, ICMP_MIN_HDR_SIZE);
1151 icmp_pack= bf_packIffLess (icmp_pack, ICMP_MIN_HDR_SIZE);
1152 icmp_hdr_p= (icmp_hdr_t *)ptr2acc_data(icmp_pack);
1153 ih_type= icmp_hdr_p->ih_type;
1154 bf_afree(icmp_pack); icmp_pack= NULL;
1156 if (ih_type != ICMP_TYPE_ECHO_REQ &&
1157 ih_type != ICMP_TYPE_ECHO_REPL)
1159 bf_afree(pack);
1160 return NULL;
1163 dest= ip_hdr->ih_src;
1164 nettype= ip_nettype(dest);
1165 if (nettype != IPNT_CLASS_A && nettype != IPNT_LOCAL &&
1166 nettype != IPNT_CLASS_B && nettype != IPNT_CLASS_C)
1168 printf("icmp_err_pack: invalid source address: ");
1169 writeIpAddr(dest);
1170 printf("\n");
1171 bf_afree(pack);
1172 return NULL;
1175 /* Take the IP header and the first 64 bits of user data. */
1176 size= ntohs(ip_hdr->ih_length);
1177 if (size < ip_hdr_len || pack_len < size)
1179 printf("icmp_err_pack: wrong packet size:\n");
1180 printf("\thdrlen= %d, ih_length= %d, bufsize= %d\n",
1181 ip_hdr_len, size, pack_len);
1182 bf_afree(pack);
1183 return NULL;
1185 if (ip_hdr_len + 8 < size)
1186 size= ip_hdr_len+8;
1187 tmp_pack= bf_cut(pack, 0, size);
1188 bf_afree(pack);
1189 pack= tmp_pack;
1190 tmp_pack= NULL;
1192 /* Create a minimal size ICMP hdr. */
1193 icmp_hdr_len= offsetof(icmp_hdr_t, ih_dun);
1194 icmp_pack= bf_memreq(icmp_hdr_len);
1195 pack= bf_append(icmp_pack, pack);
1196 size += icmp_hdr_len;
1197 pack= bf_packIffLess(pack, icmp_hdr_len);
1198 icmp_hdr_p= (icmp_hdr_t *)ptr2acc_data(pack);
1199 icmp_hdr_p->ih_type= 0;
1200 icmp_hdr_p->ih_code= 0;
1201 icmp_hdr_p->ih_chksum= 0;
1202 icmp_hdr_p->ih_hun.ihh_unused= 0;
1203 icmp_hdr_p->ih_chksum= ~icmp_pack_oneCsum(pack);
1204 *icmp_hdr_pp= icmp_hdr_p;
1206 /* Create an IP header */
1207 ip_hdr_len= IP_MIN_HDR_SIZE;
1209 ip_pack= bf_memreq(ip_hdr_len);
1210 ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack);
1212 ip_hdr->ih_vers_ihl= ip_hdr_len >> 2;
1213 ip_hdr->ih_tos= 0;
1214 ip_hdr->ih_length= htons(ip_hdr_len + size);
1215 ip_hdr->ih_flags_fragoff= 0;
1216 ip_hdr->ih_ttl= ICMP_DEF_TTL;
1217 ip_hdr->ih_proto= IPPROTO_ICMP;
1218 ip_hdr->ih_dst= dest;
1220 assert(ip_pack->acc_next == NULL);
1221 ip_pack->acc_next= pack;
1222 return ip_pack;
1226 * $PchId: icmp.c,v 1.23 2005/06/28 14:16:56 philip Exp $