/* icmp.c Copyright 1995 Philip Homburg */ #include "inet.h" #include "buf.h" #include "event.h" #include "type.h" #include "assert.h" #include "icmp.h" #include "icmp_lib.h" #include "io.h" #include "ip.h" #include "ip_int.h" #include "ipr.h" THIS_FILE typedef struct icmp_port { int icp_flags; int icp_state; int icp_ipport; int icp_ipfd; acc_t *icp_head_queue; acc_t *icp_tail_queue; acc_t *icp_write_pack; } icmp_port_t; #define ICPF_EMPTY 0x0 #define ICPF_SUSPEND 0x1 #define ICPF_READ_IP 0x2 #define ICPF_READ_SP 0x4 #define ICPF_WRITE_IP 0x8 #define ICPF_WRITE_SP 0x10 #define ICPS_BEGIN 0 #define ICPS_IPOPT 1 #define ICPS_MAIN 2 #define ICPS_ERROR 3 PRIVATE icmp_port_t *icmp_port_table; FORWARD void icmp_main ARGS(( icmp_port_t *icmp_port )); FORWARD acc_t *icmp_getdata ARGS(( int port, size_t offset, size_t count, int for_ioctl )); FORWARD int icmp_putdata ARGS(( int port, size_t offset, acc_t *data, int for_ioctl )); FORWARD void icmp_read ARGS(( icmp_port_t *icmp_port )); FORWARD void process_data ARGS(( icmp_port_t *icmp_port, acc_t *data )); FORWARD u16_t icmp_pack_oneCsum ARGS(( acc_t *ip_pack )); FORWARD void icmp_echo_request ARGS(( icmp_port_t *icmp_port, acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr, acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); FORWARD void icmp_dst_unreach ARGS(( icmp_port_t *icmp_port, acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr, acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); FORWARD void icmp_time_exceeded ARGS(( icmp_port_t *icmp_port, acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr, acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); FORWARD void icmp_router_advertisement ARGS(( icmp_port_t *icmp_port, acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); FORWARD void icmp_redirect ARGS(( icmp_port_t *icmp_port, ip_hdr_t *ip_hdr, acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); FORWARD acc_t *make_repl_ip ARGS(( ip_hdr_t *ip_hdr, int ip_len )); FORWARD void enqueue_pack ARGS(( icmp_port_t *icmp_port, acc_t *reply_ip_hdr )); FORWARD void icmp_write ARGS(( icmp_port_t *icmp_port )); FORWARD void icmp_buffree ARGS(( int priority )); FORWARD acc_t *icmp_err_pack ARGS(( acc_t *pack, icmp_hdr_t **icmp_hdr )); #ifdef BUF_CONSISTENCY_CHECK FORWARD void icmp_bufcheck ARGS(( void )); #endif PUBLIC void icmp_prep() { icmp_port_table= alloc(ip_conf_nr * sizeof(icmp_port_table[0])); } PUBLIC void icmp_init() { int i; icmp_port_t *icmp_port; assert (BUF_S >= sizeof (nwio_ipopt_t)); for (i= 0, icmp_port= icmp_port_table; iicp_flags= ICPF_EMPTY; icmp_port->icp_state= ICPS_BEGIN; #endif icmp_port->icp_ipport= i; } #ifndef BUF_CONSISTENCY_CHECK bf_logon(icmp_buffree); #else bf_logon(icmp_buffree, icmp_bufcheck); #endif for (i= 0, icmp_port= icmp_port_table; iicp_state) { case ICPS_BEGIN: icmp_port->icp_head_queue= 0; icmp_port->icp_ipfd= ip_open (icmp_port->icp_ipport, icmp_port->icp_ipport, icmp_getdata, icmp_putdata, 0); if (icmp_port->icp_ipfd<0) { DBLOCK(1, printf("unable to open ip_port %d\n", icmp_port->icp_ipport)); break; } icmp_port->icp_state= ICPS_IPOPT; icmp_port->icp_flags &= ~ICPF_SUSPEND; result= ip_ioctl (icmp_port->icp_ipfd, NWIOSIPOPT); if (result == NW_SUSPEND) { icmp_port->icp_flags |= ICPF_SUSPEND; break; } assert(result == NW_OK); /* falls through */ case ICPS_IPOPT: icmp_port->icp_state= ICPS_MAIN; icmp_port->icp_flags &= ~ICPF_SUSPEND; icmp_read(icmp_port); break; default: DBLOCK(1, printf("unknown state %d\n", icmp_port->icp_state)); break; } } PRIVATE acc_t *icmp_getdata(port, offset, count, for_ioctl) int port; size_t offset, count; int for_ioctl; { icmp_port_t *icmp_port; nwio_ipopt_t *ipopt; acc_t *data; int result; icmp_port= &icmp_port_table[port]; if (icmp_port->icp_flags & ICPF_WRITE_IP) { if (!count) { bf_afree(icmp_port->icp_write_pack); icmp_port->icp_write_pack= 0; result= (int)offset; if (result<0) { DBLOCK(1, printf("got write error %d\n", result)); } if (icmp_port->icp_flags & ICPF_WRITE_SP) { icmp_port->icp_flags &= ~(ICPF_WRITE_IP|ICPF_WRITE_SP); icmp_write (icmp_port); } return NW_OK; } return bf_cut(icmp_port->icp_write_pack, offset, count); } switch (icmp_port->icp_state) { case ICPS_IPOPT: if (!count) { result= (int)offset; assert(result == NW_OK); if (result < 0) { icmp_port->icp_state= ICPS_ERROR; break; } if (icmp_port->icp_flags & ICPF_SUSPEND) icmp_main(icmp_port); return NW_OK; } assert (count == sizeof (*ipopt)); data= bf_memreq (sizeof (*ipopt)); assert (data->acc_length == sizeof(*ipopt)); ipopt= (nwio_ipopt_t *)ptr2acc_data(data); ipopt->nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | NWIO_HDR_O_ANY | NWIO_RWDATALL; ipopt->nwio_proto= IPPROTO_ICMP; return data; default: DBLOCK(1, printf("unknown state %d\n", icmp_port->icp_state)); return 0; } } PRIVATE int icmp_putdata(port, offset, data, for_ioctl) int port; size_t offset; acc_t *data; int for_ioctl; { icmp_port_t *icmp_port; int result; icmp_port= &icmp_port_table[port]; if (icmp_port->icp_flags & ICPF_READ_IP) { assert (!for_ioctl); if (!data) { result= (int)offset; if (result<0) { DBLOCK(1, printf("got read error %d\n", result)); } if (icmp_port->icp_flags & ICPF_READ_SP) { icmp_port->icp_flags &= ~(ICPF_READ_IP|ICPF_READ_SP); icmp_read (icmp_port); } return NW_OK; } process_data(icmp_port, data); return NW_OK; } switch (icmp_port->icp_state) { default: DBLOCK(1, printf("unknown state %d\n", icmp_port->icp_state)); return 0; } } PRIVATE void icmp_read(icmp_port) icmp_port_t *icmp_port; { int result; assert (!(icmp_port->icp_flags & (ICPF_READ_IP|ICPF_READ_SP) || (icmp_port->icp_flags & (ICPF_READ_IP|ICPF_READ_SP)) == (ICPF_READ_IP|ICPF_READ_SP))); for (;;) { icmp_port->icp_flags |= ICPF_READ_IP; icmp_port->icp_flags &= ~ICPF_READ_SP; result= ip_read(icmp_port->icp_ipfd, ICMP_MAX_DATAGRAM); if (result == NW_SUSPEND) { icmp_port->icp_flags |= ICPF_READ_SP; return; } } } PUBLIC void icmp_snd_time_exceeded(port_nr, pack, code) int port_nr; acc_t *pack; int code; { acc_t *icmp_acc; icmp_hdr_t *icmp_hdr; icmp_port_t *icmp_port; assert(0 <= port_nr && port_nr < ip_conf_nr); icmp_port= &icmp_port_table[port_nr]; pack= icmp_err_pack(pack, &icmp_hdr); if (pack == NULL) return; icmp_hdr->ih_type= ICMP_TYPE_TIME_EXCEEDED; icmp_hdr->ih_code= code; icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, (u16_t *)&icmp_hdr->ih_type, 2); enqueue_pack(icmp_port, pack); } PUBLIC void icmp_snd_redirect(port_nr, pack, code, gw) int port_nr; acc_t *pack; int code; ipaddr_t gw; { acc_t *icmp_acc; icmp_hdr_t *icmp_hdr; icmp_port_t *icmp_port; assert(0 <= port_nr && port_nr < ip_conf_nr); icmp_port= &icmp_port_table[port_nr]; pack= icmp_err_pack(pack, &icmp_hdr); if (pack == NULL) return; icmp_hdr->ih_type= ICMP_TYPE_REDIRECT; icmp_hdr->ih_code= code; icmp_hdr->ih_hun.ihh_gateway= gw; icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, (u16_t *)&icmp_hdr->ih_type, 2); icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, (u16_t *)&icmp_hdr->ih_hun.ihh_gateway, 4); enqueue_pack(icmp_port, pack); } PUBLIC void icmp_snd_unreachable(port_nr, pack, code) int port_nr; acc_t *pack; int code; { acc_t *icmp_acc; icmp_hdr_t *icmp_hdr; icmp_port_t *icmp_port; assert(0 <= port_nr && port_nr < ip_conf_nr); icmp_port= &icmp_port_table[port_nr]; pack= icmp_err_pack(pack, &icmp_hdr); if (pack == NULL) return; icmp_hdr->ih_type= ICMP_TYPE_DST_UNRCH; icmp_hdr->ih_code= code; icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, (u16_t *)&icmp_hdr->ih_type, 2); enqueue_pack(icmp_port, pack); } PRIVATE void process_data(icmp_port, data) icmp_port_t *icmp_port; acc_t *data; { ip_hdr_t *ip_hdr; icmp_hdr_t *icmp_hdr; acc_t *icmp_data; int ip_hdr_len; size_t pack_len; /* Align entire packet */ data= bf_align(data, BUF_S, 4); data= bf_packIffLess(data, IP_MIN_HDR_SIZE); ip_hdr= (ip_hdr_t *)ptr2acc_data(data); DIFBLOCK(0x10, (ip_hdr->ih_dst & HTONL(0xf0000000)) == HTONL(0xe0000000), printf("got multicast packet\n")); ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; if (ip_hdr_len>IP_MIN_HDR_SIZE) { data= bf_packIffLess(data, ip_hdr_len); ip_hdr= (ip_hdr_t *)ptr2acc_data(data); } pack_len= bf_bufsize(data); pack_len -= ip_hdr_len; if (pack_len < ICMP_MIN_HDR_LEN) { DBLOCK(1, printf("got an incomplete icmp packet\n")); bf_afree(data); return; } icmp_data= bf_cut(data, ip_hdr_len, pack_len); icmp_data= bf_packIffLess (icmp_data, ICMP_MIN_HDR_LEN); icmp_hdr= (icmp_hdr_t *)ptr2acc_data(icmp_data); if ((u16_t)~icmp_pack_oneCsum(icmp_data)) { DBLOCK(1, printf( "got packet with bad checksum (= 0x%x, 0x%x)\n", icmp_hdr->ih_chksum, (u16_t)~icmp_pack_oneCsum(icmp_data))); bf_afree(data); bf_afree(icmp_data); return; } switch (icmp_hdr->ih_type) { case ICMP_TYPE_ECHO_REPL: break; case ICMP_TYPE_DST_UNRCH: icmp_dst_unreach (icmp_port, data, ip_hdr_len, ip_hdr, icmp_data, pack_len, icmp_hdr); break; case ICMP_TYPE_SRC_QUENCH: /* Ignore src quench ICMPs */ DBLOCK(2, printf("ignoring SRC QUENCH ICMP.\n")); break; case ICMP_TYPE_REDIRECT: icmp_redirect (icmp_port, ip_hdr, icmp_data, pack_len, icmp_hdr); break; case ICMP_TYPE_ECHO_REQ: icmp_echo_request(icmp_port, data, ip_hdr_len, ip_hdr, icmp_data, pack_len, icmp_hdr); return; case ICMP_TYPE_ROUTER_ADVER: icmp_router_advertisement(icmp_port, icmp_data, pack_len, icmp_hdr); break; case ICMP_TYPE_ROUTE_SOL: break; /* Should be handled by a routing deamon. */ case ICMP_TYPE_TIME_EXCEEDED: icmp_time_exceeded (icmp_port, data, ip_hdr_len, ip_hdr, icmp_data, pack_len, icmp_hdr); break; default: DBLOCK(1, printf("got an unknown icmp (%d) from ", icmp_hdr->ih_type); writeIpAddr(ip_hdr->ih_src); printf("\n")); break; } bf_afree(data); bf_afree(icmp_data); } PRIVATE void icmp_echo_request(icmp_port, ip_data, ip_len, ip_hdr, icmp_data, icmp_len, icmp_hdr) icmp_port_t *icmp_port; acc_t *ip_data, *icmp_data; int ip_len, icmp_len; ip_hdr_t *ip_hdr; icmp_hdr_t *icmp_hdr; { acc_t *repl_ip_hdr, *repl_icmp; icmp_hdr_t *repl_icmp_hdr; i32_t tmp_chksum; u16_t u16; if (icmp_hdr->ih_code != 0) { DBLOCK(1, printf("got an icmp echo request with unknown code (%d)\n", icmp_hdr->ih_code)); bf_afree(ip_data); bf_afree(icmp_data); return; } if (icmp_len < ICMP_MIN_HDR_LEN + sizeof(icmp_id_seq_t)) { DBLOCK(1, printf("got an incomplete icmp echo request\n")); bf_afree(ip_data); bf_afree(icmp_data); return; } repl_ip_hdr= make_repl_ip(ip_hdr, ip_len); repl_icmp= bf_memreq (ICMP_MIN_HDR_LEN); assert (repl_icmp->acc_length == ICMP_MIN_HDR_LEN); repl_icmp_hdr= (icmp_hdr_t *)ptr2acc_data(repl_icmp); repl_icmp_hdr->ih_type= ICMP_TYPE_ECHO_REPL; repl_icmp_hdr->ih_code= 0; DBLOCK(2, printf("ih_chksum= 0x%x, ih_type= 0x%x, repl->ih_type= 0x%x\n", icmp_hdr->ih_chksum, *(u16_t *)&icmp_hdr->ih_type, *(u16_t *)&repl_icmp_hdr->ih_type)); tmp_chksum= (~icmp_hdr->ih_chksum & 0xffff) - (i32_t)*(u16_t *)&icmp_hdr->ih_type+ *(u16_t *)&repl_icmp_hdr->ih_type; tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff); tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff); repl_icmp_hdr->ih_chksum= ~tmp_chksum; DBLOCK(2, printf("sending chksum 0x%x\n", repl_icmp_hdr->ih_chksum)); repl_ip_hdr->acc_next= repl_icmp; repl_icmp->acc_next= bf_cut (icmp_data, ICMP_MIN_HDR_LEN, icmp_len - ICMP_MIN_HDR_LEN); bf_afree(ip_data); bf_afree(icmp_data); enqueue_pack(icmp_port, repl_ip_hdr); } PRIVATE u16_t icmp_pack_oneCsum(icmp_pack) acc_t *icmp_pack; { u16_t prev; int odd_byte; char *data_ptr; int length; char byte_buf[2]; assert (icmp_pack); prev= 0; odd_byte= FALSE; for (; icmp_pack; icmp_pack= icmp_pack->acc_next) { data_ptr= ptr2acc_data(icmp_pack); length= icmp_pack->acc_length; if (!length) continue; if (odd_byte) { byte_buf[1]= *data_ptr; prev= oneC_sum(prev, (u16_t *)byte_buf, 2); data_ptr++; length--; odd_byte= FALSE; } if (length & 1) { odd_byte= TRUE; length--; byte_buf[0]= data_ptr[length]; } if (!length) continue; prev= oneC_sum (prev, (u16_t *)data_ptr, length); } if (odd_byte) prev= oneC_sum (prev, (u16_t *)byte_buf, 1); return prev; } PRIVATE acc_t *make_repl_ip(ip_hdr, ip_len) ip_hdr_t *ip_hdr; int ip_len; { ip_hdr_t *repl_ip_hdr; acc_t *repl; int repl_hdr_len; if (ip_len>IP_MIN_HDR_SIZE) { DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n")); ip_len= IP_MIN_HDR_SIZE; } repl_hdr_len= IP_MIN_HDR_SIZE; repl= bf_memreq(repl_hdr_len); assert (repl->acc_length == repl_hdr_len); repl_ip_hdr= (ip_hdr_t *)ptr2acc_data(repl); repl_ip_hdr->ih_vers_ihl= repl_hdr_len >> 2; repl_ip_hdr->ih_tos= ip_hdr->ih_tos; repl_ip_hdr->ih_ttl= ICMP_DEF_TTL; repl_ip_hdr->ih_proto= IPPROTO_ICMP; repl_ip_hdr->ih_dst= ip_hdr->ih_src; repl_ip_hdr->ih_flags_fragoff= 0; return repl; } PRIVATE void enqueue_pack(icmp_port, reply_ip_hdr) icmp_port_t *icmp_port; acc_t *reply_ip_hdr; { reply_ip_hdr->acc_ext_link= 0; if (icmp_port->icp_head_queue) { icmp_port->icp_tail_queue->acc_ext_link= reply_ip_hdr; } else { icmp_port->icp_head_queue= reply_ip_hdr; } reply_ip_hdr->acc_ext_link= NULL; icmp_port->icp_tail_queue= reply_ip_hdr; if (!(icmp_port->icp_flags & ICPF_WRITE_IP)) icmp_write(icmp_port); } PRIVATE void icmp_write(icmp_port) icmp_port_t *icmp_port; { int result; assert (!(icmp_port->icp_flags & ICPF_WRITE_IP)); while (icmp_port->icp_head_queue != NULL) { assert(icmp_port->icp_write_pack == NULL); icmp_port->icp_write_pack= icmp_port->icp_head_queue; icmp_port->icp_head_queue= icmp_port->icp_head_queue-> acc_ext_link; icmp_port->icp_flags |= ICPF_WRITE_IP; result= ip_write(icmp_port->icp_ipfd, bf_bufsize(icmp_port->icp_write_pack)); if (result == NW_SUSPEND) { icmp_port->icp_flags |= ICPF_WRITE_SP; return; } icmp_port->icp_flags &= ~ICPF_WRITE_IP; } } PRIVATE void icmp_buffree(priority) int priority; { acc_t *tmp_acc; int i; icmp_port_t *icmp_port; if (priority == ICMP_PRI_QUEUE) { for (i=0, icmp_port= icmp_port_table; iicp_head_queue) { tmp_acc= icmp_port->icp_head_queue; icmp_port->icp_head_queue= tmp_acc->acc_ext_link; bf_afree(tmp_acc); } } } } #ifdef BUF_CONSISTENCY_CHECK PRIVATE void icmp_bufcheck() { int i; icmp_port_t *icmp_port; acc_t *pack; for (i= 0, icmp_port= icmp_port_table; iicp_head_queue; pack; pack= pack->acc_ext_link) { bf_check_acc(pack); } bf_check_acc(icmp_port->icp_write_pack); } } #endif PRIVATE void icmp_dst_unreach(icmp_port, ip_pack, ip_hdr_len, ip_hdr, icmp_pack, icmp_len, icmp_hdr) icmp_port_t *icmp_port; acc_t *ip_pack; int ip_hdr_len; ip_hdr_t *ip_hdr; acc_t *icmp_pack; int icmp_len; icmp_hdr_t *icmp_hdr; { acc_t *old_ip_pack; ip_hdr_t *old_ip_hdr; int ip_port_nr; ipaddr_t dst, mask; if (icmp_len < 8 + IP_MIN_HDR_SIZE) { DBLOCK(1, printf("dest unrch with wrong size\n")); return; } old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8); old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE); old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack); if (old_ip_hdr->ih_src != ip_hdr->ih_dst) { DBLOCK(1, printf("dest unrch based on wrong packet\n")); bf_afree(old_ip_pack); return; } ip_port_nr= icmp_port->icp_ipport; switch(icmp_hdr->ih_code) { case ICMP_NET_UNRCH: dst= old_ip_hdr->ih_dst; mask= ip_get_netmask(dst); ipr_destunrch (ip_port_nr, dst & mask, mask, IPR_UNRCH_TIMEOUT); break; case ICMP_HOST_UNRCH: ipr_destunrch (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1, IPR_UNRCH_TIMEOUT); break; case ICMP_PORT_UNRCH: /* At the moment we don't do anything with this information. * It should be handed to the appropriate transport layer. */ break; default: DBLOCK(1, printf("icmp_dst_unreach: got strange code %d from ", icmp_hdr->ih_code); writeIpAddr(ip_hdr->ih_src); printf("; original destination: "); writeIpAddr(old_ip_hdr->ih_dst); printf("; protocol: %d\n", old_ip_hdr->ih_proto)); break; } bf_afree(old_ip_pack); } PRIVATE void icmp_time_exceeded(icmp_port, ip_pack, ip_hdr_len, ip_hdr, icmp_pack, icmp_len, icmp_hdr) icmp_port_t *icmp_port; acc_t *ip_pack; int ip_hdr_len; ip_hdr_t *ip_hdr; acc_t *icmp_pack; int icmp_len; icmp_hdr_t *icmp_hdr; { acc_t *old_ip_pack; ip_hdr_t *old_ip_hdr; int ip_port_nr; if (icmp_len < 8 + IP_MIN_HDR_SIZE) { DBLOCK(1, printf("time exceeded with wrong size\n")); return; } old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8); old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE); old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack); if (old_ip_hdr->ih_src != ip_hdr->ih_dst) { DBLOCK(1, printf("time exceeded based on wrong packet\n")); bf_afree(old_ip_pack); return; } ip_port_nr= icmp_port->icp_ipport; switch(icmp_hdr->ih_code) { case ICMP_TTL_EXC: ipr_ttl_exc (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1, IPR_TTL_TIMEOUT); break; case ICMP_FRAG_REASSEM: /* Ignore reassembly time-outs. */ break; default: DBLOCK(1, printf("got strange code: %d\n", icmp_hdr->ih_code)); break; } bf_afree(old_ip_pack); } PRIVATE void icmp_router_advertisement(icmp_port, icmp_pack, icmp_len, icmp_hdr) icmp_port_t *icmp_port; acc_t *icmp_pack; int icmp_len; icmp_hdr_t *icmp_hdr; { int entries; int entry_size; u16_t lifetime; int i; char *bufp; if (icmp_len < 8) { DBLOCK(1, printf("router advertisement with wrong size (%d)\n", icmp_len)); return; } if (icmp_hdr->ih_code != 0) { DBLOCK(1, printf("router advertisement with wrong code (%d)\n", icmp_hdr->ih_code)); return; } entries= icmp_hdr->ih_hun.ihh_ram.iram_na; entry_size= icmp_hdr->ih_hun.ihh_ram.iram_aes * 4; if (entries < 1) { DBLOCK(1, printf( "router advertisement with wrong number of entries (%d)\n", entries)); return; } if (entry_size < 8) { DBLOCK(1, printf( "router advertisement with wrong entry size (%d)\n", entry_size)); return; } if (icmp_len < 8 + entries * entry_size) { DBLOCK(1, printf("router advertisement with wrong size\n"); printf( "\t(entries= %d, entry_size= %d, icmp_len= %d)\n", entries, entry_size, icmp_len)); return; } lifetime= ntohs(icmp_hdr->ih_hun.ihh_ram.iram_lt); if (lifetime > 9000) { DBLOCK(1, printf( "router advertisement with wrong lifetime (%d)\n", lifetime)); return; } for (i= 0, bufp= (char *)&icmp_hdr->ih_dun.uhd_data[0]; i< entries; i++, bufp += entry_size) { ipr_add_oroute(icmp_port->icp_ipport, HTONL(0L), HTONL(0L), *(ipaddr_t *)bufp, lifetime * HZ, 1, 0, ntohl(*(i32_t *)(bufp+4)), NULL); } } PRIVATE void icmp_redirect(icmp_port, ip_hdr, icmp_pack, icmp_len, icmp_hdr) icmp_port_t *icmp_port; ip_hdr_t *ip_hdr; acc_t *icmp_pack; int icmp_len; icmp_hdr_t *icmp_hdr; { acc_t *old_ip_pack; ip_hdr_t *old_ip_hdr; int ip_port_nr; ipaddr_t dst, mask; if (icmp_len < 8 + IP_MIN_HDR_SIZE) { DBLOCK(1, printf("redirect with wrong size\n")); return; } old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8); old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE); old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack); ip_port_nr= icmp_port->icp_ipport; switch(icmp_hdr->ih_code) { case ICMP_REDIRECT_NET: dst= old_ip_hdr->ih_dst; mask= ip_get_netmask(dst); ipr_redirect (ip_port_nr, dst & mask, mask, ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway, IPR_REDIRECT_TIMEOUT); break; case ICMP_REDIRECT_HOST: ipr_redirect (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1, ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway, IPR_REDIRECT_TIMEOUT); break; default: DBLOCK(1, printf("got strange code: %d\n", icmp_hdr->ih_code)); break; } bf_afree(old_ip_pack); } PRIVATE acc_t *icmp_err_pack(pack, icmp_hdr) acc_t *pack; icmp_hdr_t **icmp_hdr; { ip_hdr_t *ip_hdr; acc_t *ip_pack, *icmp_pack, *tmp_pack; int ip_hdr_len, icmp_hdr_len; size_t size; ipaddr_t dest, netmask; nettype_t nettype; pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); /* If the IP protocol is ICMP or the fragment offset is non-zero, * drop the packet. Also check if the source address is valid. */ if (ip_hdr->ih_proto == IPPROTO_ICMP || (ntohs(ip_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) != 0) { bf_afree(pack); return NULL; } dest= ip_hdr->ih_src; nettype= ip_nettype(dest); netmask= ip_netmask(nettype); if ((nettype != IPNT_CLASS_A && nettype != IPNT_LOCAL && nettype != IPNT_CLASS_B && nettype != IPNT_CLASS_C) || (dest & ~netmask) == 0 || (dest & ~netmask) == ~netmask) { #if !CRAMPED printf("icmp_err_pack: invalid source address: "); writeIpAddr(dest); printf("\n"); #endif bf_afree(pack); return NULL; } /* Take the IP header and the first 64 bits of user data. */ size= ntohs(ip_hdr->ih_length); ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; if (size < ip_hdr_len || bf_bufsize(pack) < size) { #if !CRAMPED printf("icmp_err_pack: wrong packet size:\n"); printf("\thdrlen= %d, ih_length= %d, bufsize= %d\n", ip_hdr_len, size, bf_bufsize(pack)); #endif bf_afree(pack); return NULL; } if (ip_hdr_len + 8 < size) size= ip_hdr_len+8; tmp_pack= bf_cut(pack, 0, size); bf_afree(pack); pack= tmp_pack; tmp_pack= NULL; /* Create a minimal size ICMP hdr. */ icmp_hdr_len= offsetof(icmp_hdr_t, ih_dun); icmp_pack= bf_memreq(icmp_hdr_len); pack= bf_append(icmp_pack, pack); size += icmp_hdr_len; pack= bf_packIffLess(pack, icmp_hdr_len); *icmp_hdr= (icmp_hdr_t *)ptr2acc_data(pack); (*icmp_hdr)->ih_type= 0; (*icmp_hdr)->ih_code= 0; (*icmp_hdr)->ih_chksum= 0; (*icmp_hdr)->ih_hun.ihh_unused= 0; (*icmp_hdr)->ih_chksum= ~icmp_pack_oneCsum(pack); /* Create an IP header */ ip_hdr_len= IP_MIN_HDR_SIZE; ip_pack= bf_memreq(ip_hdr_len); ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack); ip_hdr->ih_vers_ihl= ip_hdr_len >> 2; ip_hdr->ih_tos= 0; ip_hdr->ih_length= htons(ip_hdr_len + size); ip_hdr->ih_flags_fragoff= 0; ip_hdr->ih_ttl= ICMP_DEF_TTL; ip_hdr->ih_proto= IPPROTO_ICMP; ip_hdr->ih_dst= dest; assert(ip_pack->acc_next == NULL); ip_pack->acc_next= pack; return ip_pack; } /* * $PchId: icmp.c,v 1.8 1996/12/17 07:53:34 philip Exp $ */