source: trunk/minix/servers/inet/generic/icmp.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 28.2 KB
Line 
1/*
2icmp.c
3
4Copyright 1995 Philip Homburg
5*/
6
7#include "inet.h"
8#include "buf.h"
9#include "event.h"
10#include "type.h"
11
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"
20
21THIS_FILE
22
23typedef struct icmp_port
24{
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;
37
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
44
45#define ICPS_BEGIN 0
46#define ICPS_IPOPT 1
47#define ICPS_MAIN 2
48#define ICPS_ERROR 3
49
50PRIVATE icmp_port_t *icmp_port_table;
51
52FORWARD void icmp_main ARGS(( icmp_port_t *icmp_port ));
53FORWARD acc_t *icmp_getdata ARGS(( int port, size_t offset,
54 size_t count, int for_ioctl ));
55FORWARD int icmp_putdata ARGS(( int port, size_t offset,
56 acc_t *data, int for_ioctl ));
57FORWARD void icmp_read ARGS(( icmp_port_t *icmp_port ));
58FORWARD void process_data ARGS(( icmp_port_t *icmp_port,
59 acc_t *data ));
60FORWARD u16_t icmp_pack_oneCsum ARGS(( acc_t *ip_pack ));
61FORWARD 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 ));
64FORWARD 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 ));
67FORWARD 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 ));
70FORWARD void icmp_router_advertisement ARGS(( icmp_port_t *icmp_port,
71 acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr ));
72FORWARD 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 ));
75FORWARD acc_t *make_repl_ip ARGS(( ip_hdr_t *ip_hdr,
76 int ip_len ));
77FORWARD void enqueue_pack ARGS(( icmp_port_t *icmp_port,
78 acc_t *reply_ip_hdr ));
79FORWARD int icmp_rate_limit ARGS(( icmp_port_t *icmp_port,
80 acc_t *reply_ip_hdr ));
81FORWARD void icmp_write ARGS(( event_t *ev, ev_arg_t ev_arg ));
82FORWARD void icmp_buffree ARGS(( int priority ));
83FORWARD acc_t *icmp_err_pack ARGS(( acc_t *pack, icmp_hdr_t **icmp_hdr_pp ));
84#ifdef BUF_CONSISTENCY_CHECK
85FORWARD void icmp_bufcheck ARGS(( void ));
86#endif
87
88PUBLIC void icmp_prep()
89{
90 icmp_port_table= alloc(ip_conf_nr * sizeof(icmp_port_table[0]));
91}
92
93PUBLIC void icmp_init()
94{
95 int i;
96 icmp_port_t *icmp_port;
97
98 assert (BUF_S >= sizeof (nwio_ipopt_t));
99
100 for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++)
101 {
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);
109 }
110
111#ifndef BUF_CONSISTENCY_CHECK
112 bf_logon(icmp_buffree);
113#else
114 bf_logon(icmp_buffree, icmp_bufcheck);
115#endif
116
117 for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++)
118 {
119 icmp_main (icmp_port);
120 }
121}
122
123PRIVATE void icmp_main(icmp_port)
124icmp_port_t *icmp_port;
125{
126 int result;
127 switch (icmp_port->icp_state)
128 {
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)
135 {
136 DBLOCK(1, printf("unable to open ip_port %d\n",
137 icmp_port->icp_ipport));
138 break;
139 }
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)
144 {
145 icmp_port->icp_flags |= ICPF_SUSPEND;
146 break;
147 }
148 assert(result == NW_OK);
149
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;
160 }
161}
162
163PRIVATE acc_t *icmp_getdata(port, offset, count, for_ioctl)
164int port;
165size_t offset, count;
166int for_ioctl;
167{
168 icmp_port_t *icmp_port;
169 nwio_ipopt_t *ipopt;
170 acc_t *data;
171 int result;
172 ev_arg_t ev_arg;
173
174 icmp_port= &icmp_port_table[port];
175
176 if (icmp_port->icp_flags & ICPF_WRITE_IP)
177 {
178 if (!count)
179 {
180 bf_afree(icmp_port->icp_write_pack);
181 icmp_port->icp_write_pack= 0;
182
183 result= (int)offset;
184 if (result<0)
185 {
186 DBLOCK(1, printf("got write error %d\n",
187 result));
188 }
189 if (icmp_port->icp_flags & ICPF_WRITE_SP)
190 {
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);
195 }
196 return NW_OK;
197 }
198 return bf_cut(icmp_port->icp_write_pack, offset, count);
199 }
200 switch (icmp_port->icp_state)
201 {
202 case ICPS_IPOPT:
203 if (!count)
204 {
205 result= (int)offset;
206 assert(result == NW_OK);
207 if (result < 0)
208 {
209 icmp_port->icp_state= ICPS_ERROR;
210 break;
211 }
212 if (icmp_port->icp_flags & ICPF_SUSPEND)
213 icmp_main(icmp_port);
214 return NW_OK;
215 }
216
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;
227 }
228 DBLOCK(1, printf("unknown state %d\n", icmp_port->icp_state));
229 return NULL;
230}
231
232PRIVATE int icmp_putdata(port, offset, data, for_ioctl)
233int port;
234size_t offset;
235acc_t *data;
236int for_ioctl;
237{
238 icmp_port_t *icmp_port;
239 int result;
240
241 icmp_port= &icmp_port_table[port];
242
243 if (icmp_port->icp_flags & ICPF_READ_IP)
244 {
245 if (!data)
246 {
247 result= (int)offset;
248 if (result<0)
249 {
250 DBLOCK(1, printf("got read error %d\n",
251 result));
252 }
253 if (icmp_port->icp_flags & ICPF_READ_SP)
254 {
255 icmp_port->icp_flags &=
256 ~(ICPF_READ_IP|ICPF_READ_SP);
257 icmp_read (icmp_port);
258 }
259 return NW_OK;
260 }
261 process_data(icmp_port, data);
262 return NW_OK;
263 }
264 switch (icmp_port->icp_state)
265 {
266 default:
267 DBLOCK(1, printf("unknown state %d\n",
268 icmp_port->icp_state));
269 return 0;
270 }
271}
272
273PRIVATE void icmp_read(icmp_port)
274icmp_port_t *icmp_port;
275{
276 int result;
277
278 for (;;)
279 {
280 icmp_port->icp_flags |= ICPF_READ_IP;
281 icmp_port->icp_flags &= ~ICPF_READ_SP;
282
283 result= ip_read(icmp_port->icp_ipfd, ICMP_MAX_DATAGRAM);
284 if (result == NW_SUSPEND)
285 {
286 icmp_port->icp_flags |= ICPF_READ_SP;
287 return;
288 }
289 }
290}
291
292PUBLIC void icmp_snd_time_exceeded(port_nr, pack, code)
293int port_nr;
294acc_t *pack;
295int code;
296{
297 icmp_hdr_t *icmp_hdr;
298 icmp_port_t *icmp_port;
299
300 if (port_nr >= 0 && port_nr < ip_conf_nr)
301 icmp_port= &icmp_port_table[port_nr];
302 else
303 {
304 printf("icmp_snd_time_exceeded: strange port %d\n", port_nr);
305 bf_afree(pack);
306 return;
307 }
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);
316}
317
318PUBLIC void icmp_snd_redirect(port_nr, pack, code, gw)
319int port_nr;
320acc_t *pack;
321int code;
322ipaddr_t gw;
323{
324 icmp_hdr_t *icmp_hdr;
325 icmp_port_t *icmp_port;
326
327 if (port_nr >= 0 && port_nr < ip_conf_nr)
328 icmp_port= &icmp_port_table[port_nr];
329 else
330 {
331 printf("icmp_snd_redirect: strange port %d\n", port_nr);
332 bf_afree(pack);
333 return;
334 }
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);
346}
347
348PUBLIC void icmp_snd_unreachable(port_nr, pack, code)
349int port_nr;
350acc_t *pack;
351int code;
352{
353 icmp_hdr_t *icmp_hdr;
354 icmp_port_t *icmp_port;
355
356 if (port_nr >= 0 && port_nr < ip_conf_nr)
357 icmp_port= &icmp_port_table[port_nr];
358 else
359 {
360 printf("icmp_snd_unreachable: strange port %d\n", port_nr);
361 bf_afree(pack);
362 return;
363 }
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);
372}
373
374PUBLIC void icmp_snd_mtu(port_nr, pack, mtu)
375int port_nr;
376acc_t *pack;
377u16_t mtu;
378{
379 icmp_hdr_t *icmp_hdr;
380 icmp_port_t *icmp_port;
381
382 if (port_nr >= 0 && port_nr < ip_conf_nr)
383 icmp_port= &icmp_port_table[port_nr];
384 else
385 {
386 printf("icmp_snd_mtu: strange port %d\n", port_nr);
387 bf_afree(pack);
388 return;
389 }
390
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);
402}
403
404PRIVATE void process_data(icmp_port, data)
405icmp_port_t *icmp_port;
406acc_t *data;
407{
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;
413
414 /* Align entire packet */
415 data= bf_align(data, BUF_S, 4);
416
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;
422
423 if (ip_hdr_len>IP_MIN_HDR_SIZE)
424 {
425 data= bf_packIffLess(data, ip_hdr_len);
426 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
427 }
428
429 pack_len= bf_bufsize(data);
430 pack_len -= ip_hdr_len;
431 if (pack_len < ICMP_MIN_HDR_SIZE)
432 {
433 if (pack_len == 0 && ip_hdr->ih_proto == 0)
434 {
435 /* IP layer reports new ip address, which can be
436 * ignored.
437 */
438 }
439 else
440 DBLOCK(1, printf("got an incomplete icmp packet\n"));
441 bf_afree(data);
442 return;
443 }
444
445 icmp_data= bf_cut(data, ip_hdr_len, pack_len);
446
447 icmp_data= bf_packIffLess (icmp_data, ICMP_MIN_HDR_SIZE);
448 icmp_hdr= (icmp_hdr_t *)ptr2acc_data(icmp_data);
449
450 if ((u16_t)~icmp_pack_oneCsum(icmp_data))
451 {
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;
459 }
460
461 switch (icmp_hdr->ih_type)
462 {
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;
496 }
497 bf_afree(data);
498 bf_afree(icmp_data);
499}
500
501PRIVATE void icmp_echo_request(icmp_port, ip_data, ip_len, ip_hdr,
502 icmp_data, icmp_len, icmp_hdr)
503icmp_port_t *icmp_port;
504acc_t *ip_data, *icmp_data;
505int ip_len, icmp_len;
506ip_hdr_t *ip_hdr;
507icmp_hdr_t *icmp_hdr;
508{
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;
514
515 if (icmp_hdr->ih_code != 0)
516 {
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;
523 }
524 if (icmp_len < ICMP_MIN_HDR_SIZE + sizeof(icmp_id_seq_t))
525 {
526 DBLOCK(1, printf("got an incomplete icmp echo request\n"));
527 bf_afree(ip_data);
528 bf_afree(icmp_data);
529 return;
530 }
531 tmpaddr= ntohl(ip_hdr->ih_dst);
532 if ((tmpaddr & 0xe0000000) == 0xe0000000 &&
533 tmpaddr != 0xffffffff)
534 {
535 /* Respond only to the all hosts multicast address until
536 * a decent listening service has been implemented
537 */
538 if (tmpaddr != 0xe0000001)
539 {
540 bf_afree(ip_data);
541 bf_afree(icmp_data);
542 return;
543 }
544 }
545
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)
553 {
554 /* Directed broadcast */
555 bf_afree(ip_data);
556 bf_afree(icmp_data);
557 return;
558 }
559
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;
565
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));
577
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);
581
582 bf_afree(ip_data);
583 bf_afree(icmp_data);
584
585 enqueue_pack(icmp_port, repl_ip_hdr);
586}
587
588PRIVATE u16_t icmp_pack_oneCsum(icmp_pack)
589acc_t *icmp_pack;
590{
591 u16_t prev;
592 int odd_byte;
593 char *data_ptr;
594 int length;
595 char byte_buf[2];
596
597 prev= 0;
598
599 odd_byte= FALSE;
600 for (; icmp_pack; icmp_pack= icmp_pack->acc_next)
601 {
602 data_ptr= ptr2acc_data(icmp_pack);
603 length= icmp_pack->acc_length;
604
605 if (!length)
606 continue;
607 if (odd_byte)
608 {
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;
614 }
615 if (length & 1)
616 {
617 odd_byte= TRUE;
618 length--;
619 byte_buf[0]= data_ptr[length];
620 }
621 if (!length)
622 continue;
623 prev= oneC_sum (prev, (u16_t *)data_ptr, length);
624 }
625 if (odd_byte)
626 prev= oneC_sum (prev, (u16_t *)byte_buf, 1);
627 return prev;
628}
629
630PRIVATE acc_t *make_repl_ip(ip_hdr, ip_len)
631ip_hdr_t *ip_hdr;
632int ip_len;
633{
634 ip_hdr_t *repl_ip_hdr;
635 acc_t *repl;
636 int repl_hdr_len;
637
638 if (ip_len>IP_MIN_HDR_SIZE)
639 {
640 DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n"));
641 ip_len= IP_MIN_HDR_SIZE;
642 }
643
644 repl_hdr_len= IP_MIN_HDR_SIZE;
645
646 repl= bf_memreq(repl_hdr_len);
647
648 repl_ip_hdr= (ip_hdr_t *)ptr2acc_data(repl);
649
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;
656
657 return repl;
658}
659
660PRIVATE void enqueue_pack(icmp_port, reply_ip_hdr)
661icmp_port_t *icmp_port;
662acc_t *reply_ip_hdr;
663{
664 int r;
665 ev_arg_t ev_arg;
666
667 /* Check rate */
668 if (icmp_port->icp_rate_count >= ICMP_MAX_RATE)
669 {
670 /* Something is going wrong; check policy */
671 r= icmp_rate_limit(icmp_port, reply_ip_hdr);
672 if (r == -1)
673 {
674 bf_afree(reply_ip_hdr);
675 reply_ip_hdr= NULL;
676 return;
677 }
678
679 /* OK, continue */
680 }
681 icmp_port->icp_rate_count++;
682
683 reply_ip_hdr->acc_ext_link= 0;
684
685 if (icmp_port->icp_head_queue)
686 {
687 icmp_port->icp_tail_queue->acc_ext_link=
688 reply_ip_hdr;
689 }
690 else
691 {
692 icmp_port->icp_head_queue= reply_ip_hdr;
693 }
694 reply_ip_hdr->acc_ext_link= NULL;
695 icmp_port->icp_tail_queue= reply_ip_hdr;
696
697 if (!(icmp_port->icp_flags & ICPF_WRITE_IP))
698 {
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);
702 }
703}
704
705PRIVATE int icmp_rate_limit(icmp_port, reply_ip_hdr)
706icmp_port_t *icmp_port;
707acc_t *reply_ip_hdr;
708{
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;
714
715 /* Check the time first */
716 t= get_time();
717 if (t >= icmp_port->icp_rate_lasttime + ICMP_RATE_INTERVAL)
718 {
719 icmp_port->icp_rate_lasttime= t;
720 icmp_port->icp_rate_count= 0;
721 return 0;
722 }
723
724 icmp_port->icp_rate_count++;
725
726 /* Adjust report limit if necessary */
727 if (icmp_port->icp_rate_count >
728 icmp_port->icp_rate_report+ICMP_RATE_WARN)
729 {
730 icmp_port->icp_rate_report *= 2;
731 return -1;
732 }
733
734 /* Do we need to report */
735 if (icmp_port->icp_rate_count < icmp_port->icp_rate_report)
736 return -1;
737
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)
751 {
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;
770 }
771 bf_afree(pack); pack= NULL;
772
773 return -1;
774}
775
776PRIVATE void icmp_write(ev, ev_arg)
777event_t *ev;
778ev_arg_t ev_arg;
779{
780 int result;
781 icmp_port_t *icmp_port;
782 acc_t *data;
783
784 icmp_port= ev_arg.ev_ptr;
785 assert(ev == &icmp_port->icp_event);
786
787 assert (icmp_port->icp_flags & ICPF_WRITE_IP);
788 assert (!(icmp_port->icp_flags & ICPF_WRITE_SP));
789
790 while (icmp_port->icp_head_queue != NULL)
791 {
792 data= icmp_port->icp_head_queue;
793 icmp_port->icp_head_queue= data->acc_ext_link;
794
795 result= ip_send(icmp_port->icp_ipfd, data,
796 bf_bufsize(data));
797 if (result != NW_WOULDBLOCK)
798 {
799 if (result == NW_OK)
800 continue;
801 DBLOCK(1, printf("icmp_write: error %d\n", result););
802 continue;
803 }
804
805 assert(icmp_port->icp_write_pack == NULL);
806 icmp_port->icp_write_pack= data;
807
808 result= ip_write(icmp_port->icp_ipfd,
809 bf_bufsize(icmp_port->icp_write_pack));
810 if (result == NW_SUSPEND)
811 {
812 icmp_port->icp_flags |= ICPF_WRITE_SP;
813 return;
814 }
815 }
816 icmp_port->icp_flags &= ~ICPF_WRITE_IP;
817}
818
819PRIVATE void icmp_buffree(priority)
820int priority;
821{
822 acc_t *tmp_acc;
823 int i;
824 icmp_port_t *icmp_port;
825
826 if (priority == ICMP_PRI_QUEUE)
827 {
828 for (i=0, icmp_port= icmp_port_table; i<ip_conf_nr;
829 i++, icmp_port++)
830 {
831 while(icmp_port->icp_head_queue)
832 {
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);
837 }
838 }
839 }
840}
841
842#ifdef BUF_CONSISTENCY_CHECK
843PRIVATE void icmp_bufcheck()
844{
845 int i;
846 icmp_port_t *icmp_port;
847 acc_t *pack;
848
849 for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++)
850 {
851 for (pack= icmp_port->icp_head_queue; pack;
852 pack= pack->acc_ext_link)
853 {
854 bf_check_acc(pack);
855 }
856 bf_check_acc(icmp_port->icp_write_pack);
857 }
858}
859#endif
860
861PRIVATE void icmp_dst_unreach(icmp_port, ip_pack, ip_hdr_len, ip_hdr, icmp_pack,
862 icmp_len, icmp_hdr)
863icmp_port_t *icmp_port;
864acc_t *ip_pack;
865int ip_hdr_len;
866ip_hdr_t *ip_hdr;
867acc_t *icmp_pack;
868int icmp_len;
869icmp_hdr_t *icmp_hdr;
870{
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;
877
878 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
879 {
880 DBLOCK(1, printf("dest unrch with wrong size\n"));
881 return;
882 }
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);
886
887 if (old_ip_hdr->ih_src != ip_hdr->ih_dst)
888 {
889 DBLOCK(1, printf("dest unrch based on wrong packet\n"));
890 bf_afree(old_ip_pack);
891 return;
892 }
893
894 ip_port_nr= icmp_port->icp_ipport;
895
896 switch(icmp_hdr->ih_code)
897 {
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.
911 */
912 break;
913 case ICMP_FRAGM_AND_DF:
914
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;
930
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;
940 }
941 bf_afree(old_ip_pack);
942}
943
944PRIVATE void icmp_time_exceeded(icmp_port, ip_pack, ip_hdr_len, ip_hdr,
945 icmp_pack, icmp_len, icmp_hdr)
946icmp_port_t *icmp_port;
947acc_t *ip_pack;
948int ip_hdr_len;
949ip_hdr_t *ip_hdr;
950acc_t *icmp_pack;
951int icmp_len;
952icmp_hdr_t *icmp_hdr;
953{
954 acc_t *old_ip_pack;
955 ip_hdr_t *old_ip_hdr;
956 int ip_port_nr;
957
958 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
959 {
960 DBLOCK(1, printf("time exceeded with wrong size\n"));
961 return;
962 }
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);
966
967 if (old_ip_hdr->ih_src != ip_hdr->ih_dst)
968 {
969 DBLOCK(1, printf("time exceeded based on wrong packet\n"));
970 bf_afree(old_ip_pack);
971 return;
972 }
973
974 ip_port_nr= icmp_port->icp_ipport;
975
976 switch(icmp_hdr->ih_code)
977 {
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;
989 }
990 bf_afree(old_ip_pack);
991}
992
993PRIVATE void icmp_router_advertisement(icmp_port, icmp_pack, icmp_len, icmp_hdr)
994icmp_port_t *icmp_port;
995acc_t *icmp_pack;
996int icmp_len;
997icmp_hdr_t *icmp_hdr;
998{
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;
1007
1008 if (icmp_len < 8)
1009 {
1010 DBLOCK(1,
1011 printf("router advertisement with wrong size (%d)\n",
1012 icmp_len));
1013 return;
1014 }
1015 if (icmp_hdr->ih_code != 0)
1016 {
1017 DBLOCK(1,
1018 printf("router advertisement with wrong code (%d)\n",
1019 icmp_hdr->ih_code));
1020 return;
1021 }
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)
1025 {
1026 DBLOCK(1, printf(
1027 "router advertisement with wrong number of entries (%d)\n",
1028 entries));
1029 return;
1030 }
1031 if (entry_size < 8)
1032 {
1033 DBLOCK(1, printf(
1034 "router advertisement with wrong entry size (%d)\n",
1035 entry_size));
1036 return;
1037 }
1038 if (icmp_len < 8 + entries * entry_size)
1039 {
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;
1046 }
1047 lifetime= ntohs(icmp_hdr->ih_hun.ihh_ram.iram_lt);
1048 if (lifetime > 9000)
1049 {
1050 DBLOCK(1, printf(
1051 "router advertisement with wrong lifetime (%d)\n",
1052 lifetime));
1053 return;
1054 }
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)
1058 {
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);
1064 }
1065}
1066
1067PRIVATE void icmp_redirect(icmp_port, ip_hdr, icmp_pack, icmp_len, icmp_hdr)
1068icmp_port_t *icmp_port;
1069ip_hdr_t *ip_hdr;
1070acc_t *icmp_pack;
1071int icmp_len;
1072icmp_hdr_t *icmp_hdr;
1073{
1074 acc_t *old_ip_pack;
1075 ip_hdr_t *old_ip_hdr;
1076 int ip_port_nr;
1077 ipaddr_t dst, mask;
1078
1079 if (icmp_len < 8 + IP_MIN_HDR_SIZE)
1080 {
1081 DBLOCK(1, printf("redirect with wrong size\n"));
1082 return;
1083 }
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);
1087
1088 ip_port_nr= icmp_port->icp_ipport;
1089
1090 switch(icmp_hdr->ih_code)
1091 {
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;
1108 }
1109 bf_afree(old_ip_pack);
1110}
1111
1112PRIVATE acc_t *icmp_err_pack(pack, icmp_hdr_pp)
1113acc_t *pack;
1114icmp_hdr_t **icmp_hdr_pp;
1115{
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;
1123
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);
1128
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.
1132 */
1133 if ((ntohs(ip_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) != 0)
1134 {
1135 bf_afree(pack);
1136 return NULL;
1137 }
1138 if (ip_hdr->ih_proto == IPPROTO_ICMP)
1139 {
1140 if (ip_hdr_len>IP_MIN_HDR_SIZE)
1141 {
1142 pack= bf_packIffLess(pack, ip_hdr_len);
1143 ip_hdr= (ip_hdr_t *)ptr2acc_data(pack);
1144 }
1145
1146 if (pack_len < ip_hdr_len+ICMP_MIN_HDR_SIZE)
1147 {
1148 bf_afree(pack);
1149 return NULL;
1150 }
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;
1156
1157 if (ih_type != ICMP_TYPE_ECHO_REQ &&
1158 ih_type != ICMP_TYPE_ECHO_REPL)
1159 {
1160 bf_afree(pack);
1161 return NULL;
1162 }
1163 }
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)
1169 {
1170 printf("icmp_err_pack: invalid source address: ");
1171 writeIpAddr(dest);
1172 printf("\n");
1173 bf_afree(pack);
1174 return NULL;
1175 }
1176
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)
1180 {
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;
1186 }
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;
1193
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;
1207
1208 /* Create an IP header */
1209 ip_hdr_len= IP_MIN_HDR_SIZE;
1210
1211 ip_pack= bf_memreq(ip_hdr_len);
1212 ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack);
1213
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;
1221
1222 assert(ip_pack->acc_next == NULL);
1223 ip_pack->acc_next= pack;
1224 return ip_pack;
1225}
1226
1227/*
1228 * $PchId: icmp.c,v 1.23 2005/06/28 14:16:56 philip Exp $
1229 */
Note: See TracBrowser for help on using the repository browser.