/* ip_read.c Copyright 1995 Philip Homburg */ #include "inet.h" #include "buf.h" #include "clock.h" #include "event.h" #include "type.h" #include "assert.h" #include "icmp_lib.h" #include "io.h" #include "ip.h" #include "ip_int.h" #include "ipr.h" THIS_FILE FORWARD ip_ass_t *find_ass_ent ARGS(( ip_port_t *ip_port, U16_t id, int proto, ipaddr_t src, ipaddr_t dst )); FORWARD acc_t *merge_frags ARGS(( acc_t *first, acc_t *second )); FORWARD int ip_frag_chk ARGS(( acc_t *pack )); FORWARD acc_t *reassemble ARGS(( ip_port_t *ip_port, acc_t *pack, ip_hdr_t *ip_hdr )); FORWARD void route_packets ARGS(( event_t *ev, ev_arg_t ev_arg )); FORWARD int broadcast_dst ARGS(( ip_port_t *ip_port, ipaddr_t dest )); PUBLIC int ip_read (fd, count) int fd; size_t count; { ip_fd_t *ip_fd; acc_t *pack; ip_fd= &ip_fd_table[fd]; if (!(ip_fd->if_flags & IFF_OPTSET)) { return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, EBADMODE, (acc_t *)0, FALSE); } ip_fd->if_rd_count= count; ip_fd->if_flags |= IFF_READ_IP; if (ip_fd->if_rdbuf_head) { if (get_time() <= ip_fd->if_exp_time) { pack= ip_fd->if_rdbuf_head; ip_fd->if_rdbuf_head= pack->acc_ext_link; ip_packet2user (ip_fd, pack, ip_fd->if_exp_time, bf_bufsize(pack)); assert(!(ip_fd->if_flags & IFF_READ_IP)); return NW_OK; } while (ip_fd->if_rdbuf_head) { pack= ip_fd->if_rdbuf_head; ip_fd->if_rdbuf_head= pack->acc_ext_link; bf_afree(pack); } } return NW_SUSPEND; } PRIVATE acc_t *reassemble (ip_port, pack, pack_hdr) ip_port_t *ip_port; acc_t *pack; ip_hdr_t *pack_hdr; { ip_ass_t *ass_ent; size_t pack_hdr_len, pack_data_len, pack_offset, tmp_offset; u16_t pack_flags_fragoff; acc_t *prev_acc, *curr_acc, *next_acc, *head_acc, *tmp_acc; ip_hdr_t *tmp_hdr; time_t first_time; ass_ent= find_ass_ent (ip_port, pack_hdr->ih_id, pack_hdr->ih_proto, pack_hdr->ih_src, pack_hdr->ih_dst); pack_flags_fragoff= ntohs(pack_hdr->ih_flags_fragoff); pack_hdr_len= (pack_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; pack_data_len= ntohs(pack_hdr->ih_length)-pack_hdr_len; pack_offset= (pack_flags_fragoff & IH_FRAGOFF_MASK)*8; pack->acc_ext_link= NULL; head_acc= ass_ent->ia_frags; ass_ent->ia_frags= NULL; if (head_acc == NULL) { ass_ent->ia_frags= pack; return NULL; } prev_acc= NULL; curr_acc= NULL; next_acc= head_acc; while(next_acc) { tmp_hdr= (ip_hdr_t *)ptr2acc_data(next_acc); tmp_offset= (ntohs(tmp_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK)*8; if (pack_offset < tmp_offset) break; prev_acc= curr_acc; curr_acc= next_acc; next_acc= next_acc->acc_ext_link; } if (curr_acc == NULL) { assert(prev_acc == NULL); assert(next_acc != NULL); curr_acc= merge_frags(pack, next_acc); head_acc= curr_acc; } else { curr_acc= merge_frags(curr_acc, pack); if (next_acc != NULL) curr_acc= merge_frags(curr_acc, next_acc); if (prev_acc != NULL) prev_acc->acc_ext_link= curr_acc; else head_acc= curr_acc; } ass_ent->ia_frags= head_acc; pack= ass_ent->ia_frags; pack_hdr= (ip_hdr_t *)ptr2acc_data(pack); pack_flags_fragoff= ntohs(pack_hdr->ih_flags_fragoff); if (!(pack_flags_fragoff & (IH_FRAGOFF_MASK|IH_MORE_FRAGS))) /* it's now a complete packet */ { first_time= ass_ent->ia_first_time; ass_ent->ia_frags= 0; ass_ent->ia_first_time= 0; while (pack->acc_ext_link) { tmp_acc= pack->acc_ext_link; pack->acc_ext_link= tmp_acc->acc_ext_link; bf_afree(tmp_acc); } if ((ass_ent->ia_min_ttl) * HZ + first_time < get_time()) { if (broadcast_dst(ip_port, pack_hdr->ih_dst)) { DBLOCK(1, printf( "ip_read'reassemble: reassembly timeout for broadcast packet\n");); bf_afree(pack); pack= NULL; return NULL; } icmp_snd_time_exceeded(ip_port->ip_port, pack, ICMP_FRAG_REASSEM); } else return pack; } return NULL; } PRIVATE acc_t *merge_frags (first, second) acc_t *first, *second; { ip_hdr_t *first_hdr, *second_hdr; size_t first_hdr_size, second_hdr_size, first_datasize, second_datasize, first_offset, second_offset; acc_t *cut_second, *tmp_acc; if (!second) { first->acc_ext_link= NULL; return first; } assert (first->acc_length >= IP_MIN_HDR_SIZE); assert (second->acc_length >= IP_MIN_HDR_SIZE); first_hdr= (ip_hdr_t *)ptr2acc_data(first); first_offset= (ntohs(first_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) * 8; first_hdr_size= (first_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; first_datasize= ntohs(first_hdr->ih_length) - first_hdr_size; second_hdr= (ip_hdr_t *)ptr2acc_data(second); second_offset= (ntohs(second_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) * 8; second_hdr_size= (second_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; second_datasize= ntohs(second_hdr->ih_length) - second_hdr_size; assert (first_hdr_size + first_datasize == bf_bufsize(first)); assert (second_hdr_size + second_datasize == bf_bufsize(second)); assert (second_offset >= first_offset); if (second_offset > first_offset+first_datasize) { DBLOCK(1, printf("ip fragments out of order\n")); first->acc_ext_link= second; return first; } if (second_offset + second_datasize <= first_offset + first_datasize) { /* May cause problems if we try to merge. */ bf_afree(first); return second; } if (!(second_hdr->ih_flags_fragoff & HTONS(IH_MORE_FRAGS))) first_hdr->ih_flags_fragoff &= ~HTONS(IH_MORE_FRAGS); second_datasize= second_offset+second_datasize-(first_offset+ first_datasize); cut_second= bf_cut(second, second_hdr_size + first_offset+ first_datasize-second_offset, second_datasize); tmp_acc= second->acc_ext_link; bf_afree(second); second= tmp_acc; first_datasize += second_datasize; first_hdr->ih_length= htons(first_hdr_size + first_datasize); first= bf_append (first, cut_second); first->acc_ext_link= second; assert (first_hdr_size + first_datasize == bf_bufsize(first)); return first; } PRIVATE ip_ass_t *find_ass_ent (ip_port, id, proto, src, dst) ip_port_t *ip_port; u16_t id; ipproto_t proto; ipaddr_t src; ipaddr_t dst; { ip_ass_t *new_ass_ent, *tmp_ass_ent; int i; acc_t *tmp_acc, *curr_acc; new_ass_ent= 0; for (i=0, tmp_ass_ent= ip_ass_table; iia_frags && tmp_ass_ent->ia_first_time) { DBLOCK(1, printf("strange ip_ass entry (can be a race condition)\n")); continue; } if ((tmp_ass_ent->ia_srcaddr == src) && (tmp_ass_ent->ia_dstaddr == dst) && (tmp_ass_ent->ia_proto == proto) && (tmp_ass_ent->ia_id == id) && (tmp_ass_ent->ia_port == ip_port)) { return tmp_ass_ent; } if (!new_ass_ent || tmp_ass_ent->ia_first_time < new_ass_ent->ia_first_time) { new_ass_ent= tmp_ass_ent; } } if (new_ass_ent->ia_frags) { DBLOCK(2, printf("old frags id= %u, proto= %u, src= ", ntohs(new_ass_ent->ia_id), new_ass_ent->ia_proto); writeIpAddr(new_ass_ent->ia_srcaddr); printf(" dst= "); writeIpAddr(new_ass_ent->ia_dstaddr); printf(": "); ip_print_frags(new_ass_ent->ia_frags); printf("\n")); curr_acc= new_ass_ent->ia_frags->acc_ext_link; while (curr_acc) { tmp_acc= curr_acc->acc_ext_link; bf_afree(curr_acc); curr_acc= tmp_acc; } curr_acc= new_ass_ent->ia_frags; new_ass_ent->ia_frags= 0; if (broadcast_dst(ip_port, new_ass_ent->ia_dstaddr)) { DBLOCK(1, printf( "ip_read'find_ass_ent: reassembly timeout for broadcast packet\n")); bf_afree(curr_acc); curr_acc= NULL; } else { icmp_snd_time_exceeded(ip_port->ip_port, curr_acc, ICMP_FRAG_REASSEM); } } new_ass_ent->ia_min_ttl= IP_MAX_TTL; new_ass_ent->ia_port= ip_port; new_ass_ent->ia_first_time= get_time(); new_ass_ent->ia_srcaddr= src; new_ass_ent->ia_dstaddr= dst; new_ass_ent->ia_proto= proto; new_ass_ent->ia_id= id; return new_ass_ent; } PRIVATE int ip_frag_chk(pack) acc_t *pack; { ip_hdr_t *ip_hdr; int hdr_len; if (pack->acc_length < sizeof(ip_hdr_t)) { DBLOCK(1, printf("wrong length\n")); return FALSE; } ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; if (pack->acc_length < hdr_len) { DBLOCK(1, printf("wrong length\n")); return FALSE; } if (((ip_hdr->ih_vers_ihl >> 4) & IH_VERSION_MASK) != IP_VERSION) { DBLOCK(1, printf("wrong version (ih_vers_ihl=0x%x)\n", ip_hdr->ih_vers_ihl)); return FALSE; } if (ntohs(ip_hdr->ih_length) != bf_bufsize(pack)) { DBLOCK(1, printf("wrong size\n")); return FALSE; } if ((u16_t)~oneC_sum(0, (u16_t *)ip_hdr, hdr_len)) { DBLOCK(1, printf("packet with wrong checksum (= %x)\n", (u16_t)~oneC_sum(0, (u16_t *)ip_hdr, hdr_len))); return FALSE; } if (hdr_len>IP_MIN_HDR_SIZE && ip_chk_hdropt((u8_t *) (ptr2acc_data(pack) + IP_MIN_HDR_SIZE), hdr_len-IP_MIN_HDR_SIZE)) { DBLOCK(1, printf("packet with wrong options\n")); return FALSE; } return TRUE; } PUBLIC void ip_packet2user (ip_fd, pack, exp_time, data_len) ip_fd_t *ip_fd; acc_t *pack; time_t exp_time; size_t data_len; { acc_t *tmp_pack; ip_hdr_t *ip_hdr; int result, ip_hdr_len; size_t transf_size; assert (ip_fd->if_flags & IFF_INUSE); if (!(ip_fd->if_flags & IFF_READ_IP)) { if (pack->acc_linkC != 1) { tmp_pack= bf_dupacc(pack); bf_afree(pack); pack= tmp_pack; tmp_pack= NULL; } pack->acc_ext_link= NULL; if (ip_fd->if_rdbuf_head == NULL) { ip_fd->if_rdbuf_head= pack; ip_fd->if_exp_time= exp_time; } else ip_fd->if_rdbuf_tail->acc_ext_link= pack; ip_fd->if_rdbuf_tail= pack; return; } assert (pack->acc_length >= IP_MIN_HDR_SIZE); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); if (ip_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY) { ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; assert (data_len > ip_hdr_len); data_len -= ip_hdr_len; pack= bf_delhead(pack, ip_hdr_len); } if (data_len > ip_fd->if_rd_count) { tmp_pack= bf_cut (pack, 0, ip_fd->if_rd_count); bf_afree(pack); pack= tmp_pack; transf_size= ip_fd->if_rd_count; } else transf_size= data_len; if (ip_fd->if_put_pkt) { (*ip_fd->if_put_pkt)(ip_fd->if_srfd, pack, transf_size); return; } result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, (size_t)0, pack, FALSE); if (result >= 0) { if (data_len > transf_size) result= EPACKSIZE; else result= transf_size; } ip_fd->if_flags &= ~IFF_READ_IP; result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result, (acc_t *)0, FALSE); assert (result >= 0); } PUBLIC void ip_port_arrive (ip_port, pack, ip_hdr) ip_port_t *ip_port; acc_t *pack; ip_hdr_t *ip_hdr; { ip_fd_t *ip_fd, *first_fd, *share_fd; unsigned long ip_pack_stat; unsigned size; int i; int hash, proto; time_t exp_time; assert (pack->acc_linkC>0); assert (pack->acc_length >= IP_MIN_HDR_SIZE); if (ntohs(ip_hdr->ih_flags_fragoff) & (IH_FRAGOFF_MASK|IH_MORE_FRAGS)) { pack= reassemble (ip_port, pack, ip_hdr); if (!pack) return; assert (pack->acc_length >= IP_MIN_HDR_SIZE); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); assert (!(ntohs(ip_hdr->ih_flags_fragoff) & (IH_FRAGOFF_MASK|IH_MORE_FRAGS))); } size= ntohs(ip_hdr->ih_length); if (size > bf_bufsize(pack)) { /* Should discard packet */ assert(0); bf_afree(pack); pack= NULL; return; } exp_time= get_time() + (ip_hdr->ih_ttl+1) * HZ; if (ip_hdr->ih_dst == ip_port->ip_ipaddr) ip_pack_stat= NWIO_EN_LOC; else ip_pack_stat= NWIO_EN_BROAD; proto= ip_hdr->ih_proto; hash= proto & (IP_PROTO_HASH_NR-1); first_fd= NULL; for (i= 0; i<2; i++) { share_fd= NULL; ip_fd= (i == 0) ? ip_port->ip_proto_any : ip_port->ip_proto[hash]; for (; ip_fd; ip_fd= ip_fd->if_proto_next) { if (i && ip_fd->if_ipopt.nwio_proto != proto) continue; if (!(ip_fd->if_ipopt.nwio_flags & ip_pack_stat)) continue; if ((ip_fd->if_ipopt.nwio_flags & NWIO_REMSPEC) && ip_hdr->ih_src != ip_fd->if_ipopt.nwio_rem) { continue; } if ((ip_fd->if_ipopt.nwio_flags & NWIO_ACC_MASK) == NWIO_SHARED) { if (!share_fd) { share_fd= ip_fd; continue; } if (!ip_fd->if_rdbuf_head) share_fd= ip_fd; continue; } if (!first_fd) { first_fd= ip_fd; continue; } pack->acc_linkC++; ip_packet2user(ip_fd, pack, exp_time, size); } if (share_fd) { pack->acc_linkC++; ip_packet2user(share_fd, pack, exp_time, size); } } if (first_fd) { if (first_fd->if_put_pkt && (first_fd->if_flags & IFF_READ_IP) && !(first_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY)) { (*first_fd->if_put_pkt)(first_fd->if_srfd, pack, size); } else ip_packet2user(first_fd, pack, exp_time, size); } else { if (ip_pack_stat == NWIO_EN_LOC) { DBLOCK(0x01, printf("ip_port_arrive: dropping packet for proto %d\n", proto)); } else { DBLOCK(0x20, printf("dropping packet for proto %d\n", proto)); } bf_afree(pack); } } PUBLIC void ip_arrived(ip_port, pack) ip_port_t *ip_port; acc_t *pack; { ip_hdr_t *ip_hdr; ipaddr_t dest; int ip_frag_len, ip_hdr_len, highbyte; size_t pack_size; acc_t *tmp_pack, *hdr_pack; ev_arg_t ev_arg; pack_size= bf_bufsize(pack); if (pack_size < IP_MIN_HDR_SIZE) { DBLOCK(1, printf("wrong acc_length\n")); bf_afree(pack); return; } pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE); assert (pack->acc_length >= IP_MIN_HDR_SIZE); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; if (ip_hdr_len>IP_MIN_HDR_SIZE) { pack= bf_packIffLess(pack, ip_hdr_len); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); } ip_frag_len= ntohs(ip_hdr->ih_length); if (ip_frag_len != pack_size) { if (pack_size < ip_frag_len) { /* Sent ICMP? */ DBLOCK(1, printf("wrong acc_length\n")); bf_afree(pack); return; } assert(ip_frag_lenih_dst; if (dest == ip_port->ip_ipaddr) { ip_port_arrive (ip_port, pack, ip_hdr); return; } if (broadcast_dst(ip_port, dest)) { ip_port_arrive (ip_port, pack, ip_hdr); return; } if (pack->acc_linkC != 1 || pack->acc_buffer->buf_linkC != 1) { /* Get a private copy of the IP header */ hdr_pack= bf_memreq(ip_hdr_len); memcpy(ptr2acc_data(hdr_pack), ip_hdr, ip_hdr_len); pack= bf_delhead(pack, ip_hdr_len); hdr_pack->acc_next= pack; pack= hdr_pack; hdr_pack= NULL; ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); } assert(pack->acc_linkC == 1); assert(pack->acc_buffer->buf_linkC == 1); /* Try to decrement the ttl field with one. */ if (ip_hdr->ih_ttl < 2) { icmp_snd_time_exceeded(ip_port->ip_port, pack, ICMP_TTL_EXC); return; } ip_hdr->ih_ttl--; ip_hdr_chksum(ip_hdr, ip_hdr_len); /* Avoid routing to bad destinations. */ highbyte= ntohl(dest) >> 24; if (highbyte == 0 || highbyte == 127 || (highbyte == 169 && (((ntohl(dest) >> 16) & 0xff) == 254)) || highbyte >= 0xe0) { /* Bogus destination address */ bf_afree(pack); return; } /* Further processing from an event handler */ if (pack->acc_linkC != 1) { tmp_pack= bf_dupacc(pack); bf_afree(pack); pack= tmp_pack; tmp_pack= NULL; } pack->acc_ext_link= NULL; if (ip_port->ip_routeq_head) { ip_port->ip_routeq_tail->acc_ext_link= pack; ip_port->ip_routeq_tail= pack; return; } ip_port->ip_routeq_head= pack; ip_port->ip_routeq_tail= pack; ev_arg.ev_ptr= ip_port; ev_enqueue(&ip_port->ip_routeq_event, route_packets, ev_arg); } PUBLIC void ip_arrived_broadcast(ip_port, pack) ip_port_t *ip_port; acc_t *pack; { ip_hdr_t *ip_hdr; int ip_frag_len, ip_hdr_len; size_t pack_size; acc_t *tmp_pack; pack_size= bf_bufsize(pack); if (pack_size < IP_MIN_HDR_SIZE) { DBLOCK(1, printf("wrong acc_length\n")); bf_afree(pack); return; } pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE); assert (pack->acc_length >= IP_MIN_HDR_SIZE); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); DIFBLOCK(0x20, (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) { pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); pack= bf_packIffLess(pack, ip_hdr_len); ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); } ip_frag_len= ntohs(ip_hdr->ih_length); if (ip_frag_lenih_dst)) { #if 0 printf( "ip[%d]: broadcast packet for ip-nonbroadcast addr, src=", ip_port->ip_port); writeIpAddr(ip_hdr->ih_src); printf(" dst="); writeIpAddr(ip_hdr->ih_dst); printf("\n"); #endif bf_afree(pack); return; } ip_port_arrive (ip_port, pack, ip_hdr); } PRIVATE void route_packets(ev, ev_arg) event_t *ev; ev_arg_t ev_arg; { ip_port_t *ip_port; ipaddr_t dest; acc_t *pack; iroute_t *iroute; ip_port_t *next_port; int r, type; ip_hdr_t *ip_hdr; size_t req_mtu; ip_port= ev_arg.ev_ptr; assert(&ip_port->ip_routeq_event == ev); while (pack= ip_port->ip_routeq_head, pack != NULL) { ip_port->ip_routeq_head= pack->acc_ext_link; ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); dest= ip_hdr->ih_dst; iroute= iroute_frag(ip_port->ip_port, dest); if (iroute == NULL || iroute->irt_dist == IRTD_UNREACHABLE) { /* Also unreachable */ /* Finding out if we send a network unreachable is too * much trouble. */ if (iroute == NULL) { printf("ip[%d]: no route to ", ip_port-ip_port_table); writeIpAddr(dest); printf("\n"); } icmp_snd_unreachable(ip_port->ip_port, pack, ICMP_HOST_UNRCH); continue; } next_port= &ip_port_table[iroute->irt_port]; if (ip_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG)) { req_mtu= bf_bufsize(pack); if (req_mtu > next_port->ip_mtu || (iroute->irt_mtu && req_mtu>iroute->irt_mtu)) { icmp_snd_mtu(ip_port->ip_port, pack, next_port->ip_mtu); continue; } } if (next_port != ip_port) { if (iroute->irt_gateway != 0) { /* Just send the packet to the next gateway */ pack->acc_linkC++; /* Extra ref for ICMP */ r= next_port->ip_dev_send(next_port, iroute->irt_gateway, pack, IP_LT_NORMAL); if (r == EDSTNOTRCH) { printf("ip[%d]: gw ", ip_port-ip_port_table); writeIpAddr(iroute->irt_gateway); printf(" on ip[%d] is down for dest ", next_port-ip_port_table); writeIpAddr(dest); printf("\n"); icmp_snd_unreachable(next_port- ip_port_table, pack, ICMP_HOST_UNRCH); pack= NULL; } else { assert(r == 0); bf_afree(pack); pack= NULL; } continue; } /* The packet is for the attached network. Special * addresses are the ip address of the interface and * net.0 if no IP_42BSD_BCAST. */ if (dest == next_port->ip_ipaddr) { ip_port_arrive (next_port, pack, ip_hdr); continue; } if (dest == iroute->irt_dest) { /* Never forward obsolete directed broadcasts */ #if IP_42BSD_BCAST && 0 type= IP_LT_BROADCAST; #else /* Bogus destination address */ DBLOCK(1, printf( "ip[%d]: dropping old-fashioned directed broadcast ", ip_port-ip_port_table); writeIpAddr(dest); printf("\n");); icmp_snd_unreachable(next_port-ip_port_table, pack, ICMP_HOST_UNRCH); continue; #endif } else if (dest == (iroute->irt_dest | ~iroute->irt_subnetmask)) { if (!ip_forward_directed_bcast) { /* Do not forward directed broadcasts */ DBLOCK(1, printf( "ip[%d]: dropping directed broadcast ", ip_port-ip_port_table); writeIpAddr(dest); printf("\n");); icmp_snd_unreachable(next_port- ip_port_table, pack, ICMP_HOST_UNRCH); continue; } else type= IP_LT_BROADCAST; } else type= IP_LT_NORMAL; /* Just send the packet to it's destination */ pack->acc_linkC++; /* Extra ref for ICMP */ r= next_port->ip_dev_send(next_port, dest, pack, type); if (r == EDSTNOTRCH) { DBLOCK(1, printf("ip[%d]: next hop ", ip_port-ip_port_table); writeIpAddr(dest); printf(" on ip[%d] is down\n", next_port-ip_port_table);); icmp_snd_unreachable(next_port-ip_port_table, pack, ICMP_HOST_UNRCH); pack= NULL; } else { assert(r == 0 || (printf("r = %d\n", r), 0)); bf_afree(pack); pack= NULL; } continue; } /* Now we know that the packet should be routed over the same * network as it came from. If there is a next hop gateway, * we can send the packet to that gateway and send a redirect * ICMP to the sender if the sender is on the attached * network. If there is no gateway complain. */ if (iroute->irt_gateway == 0) { printf("ip_arrived: packet should not be here, src="); writeIpAddr(ip_hdr->ih_src); printf(" dst="); writeIpAddr(ip_hdr->ih_dst); printf("\n"); bf_afree(pack); continue; } if (((ip_hdr->ih_src ^ ip_port->ip_ipaddr) & ip_port->ip_subnetmask) == 0) { /* Finding out if we can send a network redirect * instead of a host redirect is too much trouble. */ pack->acc_linkC++; icmp_snd_redirect(ip_port->ip_port, pack, ICMP_REDIRECT_HOST, iroute->irt_gateway); } else { printf("ip_arrived: packet is wrongly routed, src="); writeIpAddr(ip_hdr->ih_src); printf(" dst="); writeIpAddr(ip_hdr->ih_dst); printf("\n"); printf("in port %d, output %d, dest net ", ip_port->ip_port, iroute->irt_port); writeIpAddr(iroute->irt_dest); printf("/"); writeIpAddr(iroute->irt_subnetmask); printf(" next hop "); writeIpAddr(iroute->irt_gateway); printf("\n"); bf_afree(pack); continue; } /* No code for unreachable ICMPs here. The sender should * process the ICMP redirect and figure it out. */ ip_port->ip_dev_send(ip_port, iroute->irt_gateway, pack, IP_LT_NORMAL); } } PRIVATE int broadcast_dst(ip_port, dest) ip_port_t *ip_port; ipaddr_t dest; { ipaddr_t my_ipaddr, netmask, classmask; /* Treat class D (multicast) address as broadcasts. */ if ((dest & HTONL(0xF0000000)) == HTONL(0xE0000000)) { return 1; } /* Accept without complaint if netmask not yet configured. */ if (!(ip_port->ip_flags & IPF_NETMASKSET)) { return 1; } /* Two possibilities, 0 (iff IP_42BSD_BCAST) and -1 */ if (dest == HTONL((ipaddr_t)-1)) return 1; #if IP_42BSD_BCAST if (dest == HTONL((ipaddr_t)0)) return 1; #endif netmask= ip_port->ip_subnetmask; my_ipaddr= ip_port->ip_ipaddr; if (((my_ipaddr ^ dest) & netmask) != 0) { classmask= ip_port->ip_classfulmask; /* Not a subnet broadcast, maybe a classful broadcast */ if (((my_ipaddr ^ dest) & classmask) != 0) { return 0; } /* Two possibilities, net.0 (iff IP_42BSD_BCAST) and net.-1 */ if ((dest & ~classmask) == ~classmask) { return 1; } #if IP_42BSD_BCAST if ((dest & ~classmask) == 0) return 1; #endif return 0; } if (!(ip_port->ip_flags & IPF_SUBNET_BCAST)) return 0; /* No subnet broadcasts on this network */ /* Two possibilities, subnet.0 (iff IP_42BSD_BCAST) and subnet.-1 */ if ((dest & ~netmask) == ~netmask) return 1; #if IP_42BSD_BCAST if ((dest & ~netmask) == 0) return 1; #endif return 0; } void ip_process_loopb(ev, arg) event_t *ev; ev_arg_t arg; { ip_port_t *ip_port; acc_t *pack; ip_port= arg.ev_ptr; assert(ev == &ip_port->ip_loopb_event); while(pack= ip_port->ip_loopb_head, pack != NULL) { ip_port->ip_loopb_head= pack->acc_ext_link; ip_arrived(ip_port, pack); } } /* * $PchId: ip_read.c,v 1.33 2005/06/28 14:18:50 philip Exp $ */