📄 arp.c
字号:
/*arp.cCopyright 1995 Philip Homburg*/#include "inet.h"#include "type.h"#include "arp.h"#include "assert.h"#include "buf.h"#include "clock.h"#include "event.h"#include "eth.h"#include "io.h"#include "sr.h"THIS_FILE#define ARP_CACHE_NR 256#define AP_REQ_NR 32#define ARP_HASH_NR 256#define ARP_HASH_MASK 0xff#define ARP_HASH_WIDTH 4#define MAX_ARP_RETRIES 5#define ARP_TIMEOUT (HZ/2+1) /* .5 seconds */#ifndef ARP_EXP_TIME#define ARP_EXP_TIME (20L*60L*HZ) /* 20 minutes */#endif#define ARP_NOTRCH_EXP_TIME (30*HZ) /* 30 seconds */#define ARP_INUSE_OFFSET (60*HZ) /* an entry in the cache can be deleted if its not used for 1 minute */typedef struct arp46{ ether_addr_t a46_dstaddr; ether_addr_t a46_srcaddr; ether_type_t a46_ethtype; union { struct { u16_t a_hdr, a_pro; u8_t a_hln, a_pln; u16_t a_op; ether_addr_t a_sha; u8_t a_spa[4]; ether_addr_t a_tha; u8_t a_tpa[4]; } a46_data; char a46_dummy[ETH_MIN_PACK_SIZE-ETH_HDR_SIZE]; } a46_data;} arp46_t;#define a46_hdr a46_data.a46_data.a_hdr#define a46_pro a46_data.a46_data.a_pro#define a46_hln a46_data.a46_data.a_hln#define a46_pln a46_data.a46_data.a_pln#define a46_op a46_data.a46_data.a_op#define a46_sha a46_data.a46_data.a_sha#define a46_spa a46_data.a46_data.a_spa#define a46_tha a46_data.a46_data.a_tha#define a46_tpa a46_data.a46_data.a_tpatypedef struct arp_port{ int ap_flags; int ap_state; int ap_eth_port; int ap_ip_port; int ap_eth_fd; ether_addr_t ap_ethaddr; /* Ethernet address of this port */ ipaddr_t ap_ipaddr; /* IP address of this port */ struct arp_req { timer_t ar_timer; int ar_entry; int ar_req_count; } ap_req[AP_REQ_NR]; arp_func_t ap_arp_func; acc_t *ap_sendpkt; acc_t *ap_sendlist; acc_t *ap_reclist; event_t ap_event;} arp_port_t;#define APF_EMPTY 0x00#define APF_ARP_RD_IP 0x01#define APF_ARP_RD_SP 0x02#define APF_ARP_WR_IP 0x04#define APF_ARP_WR_SP 0x08#define APF_INADDR_SET 0x10#define APF_SUSPEND 0x20#define APS_INITIAL 1#define APS_GETADDR 2#define APS_ARPSTART 3#define APS_ARPPROTO 4#define APS_ARPMAIN 5#define APS_ERROR 6typedef struct arp_cache{ int ac_flags; int ac_state; ether_addr_t ac_ethaddr; ipaddr_t ac_ipaddr; arp_port_t *ac_port; time_t ac_expire; time_t ac_lastuse;} arp_cache_t;#define ACF_EMPTY 0#define ACF_PERM 1#define ACF_PUB 2#define ACS_UNUSED 0#define ACS_INCOMPLETE 1#define ACS_VALID 2#define ACS_UNREACHABLE 3PRIVATE struct arp_hash_ent{ arp_cache_t *ahe_row[ARP_HASH_WIDTH];} arp_hash[ARP_HASH_NR];PRIVATE arp_port_t *arp_port_table;PRIVATE arp_cache_t *arp_cache;PRIVATE int arp_cache_nr;FORWARD acc_t *arp_getdata ARGS(( int fd, size_t offset, size_t count, int for_ioctl ));FORWARD int arp_putdata ARGS(( int fd, size_t offset, acc_t *data, int for_ioctl ));FORWARD void arp_main ARGS(( arp_port_t *arp_port ));FORWARD void arp_timeout ARGS(( int ref, timer_t *timer ));FORWARD void setup_write ARGS(( arp_port_t *arp_port ));FORWARD void setup_read ARGS(( arp_port_t *arp_port ));FORWARD void do_reclist ARGS(( event_t *ev, ev_arg_t ev_arg ));FORWARD void process_arp_pkt ARGS(( arp_port_t *arp_port, acc_t *data ));FORWARD void client_reply ARGS(( arp_port_t *arp_port, ipaddr_t ipaddr, ether_addr_t *ethaddr ));FORWARD arp_cache_t *find_cache_ent ARGS(( arp_port_t *arp_port, ipaddr_t ipaddr ));FORWARD arp_cache_t *alloc_cache_ent ARGS(( int flags ));FORWARD void arp_buffree ARGS(( int priority ));#ifdef BUF_CONSISTENCY_CHECKFORWARD void arp_bufcheck ARGS(( void ));#endifPUBLIC void arp_prep(){ arp_port_table= alloc(eth_conf_nr * sizeof(arp_port_table[0])); arp_cache_nr= ARP_CACHE_NR; if (arp_cache_nr < (eth_conf_nr+1)*AP_REQ_NR) { arp_cache_nr= (eth_conf_nr+1)*AP_REQ_NR; printf("arp: using %d cache entries instead of %d\n", arp_cache_nr, ARP_CACHE_NR); } arp_cache= alloc(arp_cache_nr * sizeof(arp_cache[0]));}PUBLIC void arp_init(){ arp_port_t *arp_port; arp_cache_t *cache; int i; assert (BUF_S >= sizeof(struct nwio_ethstat)); assert (BUF_S >= sizeof(struct nwio_ethopt)); assert (BUF_S >= sizeof(arp46_t)); for (i=0, arp_port= arp_port_table; i<eth_conf_nr; i++, arp_port++) { arp_port->ap_state= APS_ERROR; /* Mark all ports as * unavailable */ } cache= arp_cache; for (i=0; i<arp_cache_nr; i++, cache++) { cache->ac_state= ACS_UNUSED; cache->ac_flags= ACF_EMPTY; cache->ac_expire= 0; cache->ac_lastuse= 0; }#ifndef BUF_CONSISTENCY_CHECK bf_logon(arp_buffree);#else bf_logon(arp_buffree, arp_bufcheck);#endif}PRIVATE void arp_main(arp_port)arp_port_t *arp_port;{ int result; switch (arp_port->ap_state) { case APS_INITIAL: arp_port->ap_eth_fd= eth_open(arp_port->ap_eth_port, arp_port->ap_eth_port, arp_getdata, arp_putdata, 0 /* no put_pkt */, 0 /* no select_res */); if (arp_port->ap_eth_fd<0) { DBLOCK(1, printf("arp[%d]: unable to open eth[%d]\n", arp_port-arp_port_table, arp_port->ap_eth_port)); return; } arp_port->ap_state= APS_GETADDR; result= eth_ioctl (arp_port->ap_eth_fd, NWIOGETHSTAT); if ( result == NW_SUSPEND) { arp_port->ap_flags |= APF_SUSPEND; return; } assert(result == NW_OK); /* fall through */ case APS_GETADDR: /* Wait for IP address */ if (!(arp_port->ap_flags & APF_INADDR_SET)) return; /* fall through */ case APS_ARPSTART: arp_port->ap_state= APS_ARPPROTO; result= eth_ioctl (arp_port->ap_eth_fd, NWIOSETHOPT); if (result==NW_SUSPEND) { arp_port->ap_flags |= APF_SUSPEND; return; } assert(result == NW_OK); /* fall through */ case APS_ARPPROTO: arp_port->ap_state= APS_ARPMAIN; setup_write(arp_port); setup_read(arp_port); return; default: ip_panic(( "arp_main(&arp_port_table[%d]) called but ap_state=0x%x\n", arp_port->ap_eth_port, arp_port->ap_state )); }}PRIVATE acc_t *arp_getdata (fd, offset, count, for_ioctl)int fd;size_t offset;size_t count;int for_ioctl;{ arp_port_t *arp_port; acc_t *data; int result; arp_port= &arp_port_table[fd]; switch (arp_port->ap_state) { case APS_ARPPROTO: if (!count) { result= (int)offset; if (result<0) { arp_port->ap_state= APS_ERROR; break; } if (arp_port->ap_flags & APF_SUSPEND) { arp_port->ap_flags &= ~APF_SUSPEND; arp_main(arp_port); } return NW_OK; } assert ((!offset) && (count == sizeof(struct nwio_ethopt))); { struct nwio_ethopt *ethopt; acc_t *acc; acc= bf_memreq(sizeof(*ethopt)); ethopt= (struct nwio_ethopt *)ptr2acc_data(acc); ethopt->nweo_flags= NWEO_COPY|NWEO_EN_BROAD| NWEO_TYPESPEC; ethopt->nweo_type= HTONS(ETH_ARP_PROTO); return acc; } case APS_ARPMAIN: assert (arp_port->ap_flags & APF_ARP_WR_IP); if (!count) { data= arp_port->ap_sendpkt; arp_port->ap_sendpkt= NULL; assert(data); bf_afree(data); data= NULL; result= (int)offset; if (result<0) { DIFBLOCK(1, (result != NW_SUSPEND), printf( "arp[%d]: write error on port %d: error %d\n", fd, arp_port->ap_eth_fd, result)); arp_port->ap_state= APS_ERROR; break; } arp_port->ap_flags &= ~APF_ARP_WR_IP; if (arp_port->ap_flags & APF_ARP_WR_SP) setup_write(arp_port); return NW_OK; } assert (offset+count <= sizeof(arp46_t)); data= arp_port->ap_sendpkt; assert(data); data= bf_cut(data, offset, count); return data; default: printf("arp_getdata(%d, 0x%d, 0x%d) called but ap_state=0x%x\n", fd, offset, count, arp_port->ap_state); break; } return 0;}PRIVATE int arp_putdata (fd, offset, data, for_ioctl)int fd;size_t offset;acc_t *data;int for_ioctl;{ arp_port_t *arp_port; int result; struct nwio_ethstat *ethstat; ev_arg_t ev_arg; acc_t *tmpacc; arp_port= &arp_port_table[fd]; if (arp_port->ap_flags & APF_ARP_RD_IP) { if (!data) { result= (int)offset; if (result<0) { DIFBLOCK(1, (result != NW_SUSPEND), printf( "arp[%d]: read error on port %d: error %d\n", fd, arp_port->ap_eth_fd, result)); return NW_OK; } if (arp_port->ap_flags & APF_ARP_RD_SP) { arp_port->ap_flags &= ~(APF_ARP_RD_IP| APF_ARP_RD_SP); setup_read(arp_port); } else arp_port->ap_flags &= ~(APF_ARP_RD_IP| APF_ARP_RD_SP); return NW_OK; } assert (!offset); /* Warning: the above assertion is illegal; puts and gets of data can be brokenup in any piece the server likes. However we assume that the server is eth.c and it transfers only whole packets. */ data= bf_packIffLess(data, sizeof(arp46_t)); if (data->acc_length >= sizeof(arp46_t)) { if (!arp_port->ap_reclist) { ev_arg.ev_ptr= arp_port; ev_enqueue(&arp_port->ap_event, do_reclist, ev_arg); } if (data->acc_linkC != 1) { tmpacc= bf_dupacc(data); bf_afree(data); data= tmpacc; tmpacc= NULL; } data->acc_ext_link= arp_port->ap_reclist; arp_port->ap_reclist= data; } else bf_afree(data); return NW_OK; } switch (arp_port->ap_state) { case APS_GETADDR: if (!data) { result= (int)offset; if (result<0) { arp_port->ap_state= APS_ERROR; break; } if (arp_port->ap_flags & APF_SUSPEND) { arp_port->ap_flags &= ~APF_SUSPEND; arp_main(arp_port); } return NW_OK; } compare (bf_bufsize(data), ==, sizeof(*ethstat)); data= bf_packIffLess(data, sizeof(*ethstat)); compare (data->acc_length, ==, sizeof(*ethstat)); ethstat= (struct nwio_ethstat *)ptr2acc_data(data); arp_port->ap_ethaddr= ethstat->nwes_addr; bf_afree(data); return NW_OK; default: printf("arp_putdata(%d, 0x%d, 0x%lx) called but ap_state=0x%x\n", fd, offset, (unsigned long)data, arp_port->ap_state); break; } return EGENERIC;}PRIVATE void setup_read(arp_port)arp_port_t *arp_port;{ int result; while (!(arp_port->ap_flags & APF_ARP_RD_IP)) { arp_port->ap_flags |= APF_ARP_RD_IP; result= eth_read (arp_port->ap_eth_fd, ETH_MAX_PACK_SIZE); if (result == NW_SUSPEND) { arp_port->ap_flags |= APF_ARP_RD_SP; return; } DIFBLOCK(1, (result != NW_OK), printf("arp[%d]: eth_read(..,%d)=%d\n", arp_port-arp_port_table, ETH_MAX_PACK_SIZE, result)); }}PRIVATE void setup_write(arp_port)arp_port_t *arp_port;{ int result; acc_t *data; for(;;) { data= arp_port->ap_sendlist; if (!data) break; arp_port->ap_sendlist= data->acc_ext_link; if (arp_port->ap_ipaddr == HTONL(0x00000000)) { /* Interface is down */ printf( "arp[%d]: not sending ARP packet, interface is down\n", arp_port-arp_port_table); bf_afree(data); data= NULL; continue; } assert(!arp_port->ap_sendpkt); arp_port->ap_sendpkt= data; data= NULL; arp_port->ap_flags= (arp_port->ap_flags & ~APF_ARP_WR_SP) | APF_ARP_WR_IP; result= eth_write(arp_port->ap_eth_fd, sizeof(arp46_t)); if (result == NW_SUSPEND) { arp_port->ap_flags |= APF_ARP_WR_SP; break; } if (result<0) { DIFBLOCK(1, (result != NW_SUSPEND), printf("arp[%d]: eth_write(..,%d)=%d\n", arp_port-arp_port_table, sizeof(arp46_t), result)); return; } }}PRIVATE void do_reclist(ev, ev_arg)event_t *ev;ev_arg_t ev_arg;{ arp_port_t *arp_port; acc_t *data; arp_port= ev_arg.ev_ptr; assert(ev == &arp_port->ap_event); while (data= arp_port->ap_reclist, data != NULL) { arp_port->ap_reclist= data->acc_ext_link; process_arp_pkt(arp_port, data); bf_afree(data); }}PRIVATE void process_arp_pkt (arp_port, data)arp_port_t *arp_port;acc_t *data;{ int i, entry, do_reply; arp46_t *arp; u16_t *p; arp_cache_t *ce, *cache; struct arp_req *reqp; time_t curr_time; ipaddr_t spa, tpa; curr_time= get_time(); arp= (arp46_t *)ptr2acc_data(data); memcpy(&spa, arp->a46_spa, sizeof(ipaddr_t)); memcpy(&tpa, arp->a46_tpa, sizeof(ipaddr_t)); if (arp->a46_hdr != HTONS(ARP_ETHERNET) || arp->a46_hln != 6 || arp->a46_pro != HTONS(ETH_IP_PROTO) || arp->a46_pln != 4) return; if (arp_port->ap_ipaddr == HTONL(0x00000000)) { /* Interface is down */#if DEBUG printf("arp[%d]: dropping ARP packet, interface is down\n", arp_port-arp_port_table);#endif return; } ce= find_cache_ent(arp_port, spa); cache= NULL; /* lint */ do_reply= 0; if (arp->a46_op != HTONS(ARP_REQUEST)) ; /* No need to reply */ else if (tpa == arp_port->ap_ipaddr) do_reply= 1; else { /* Look for a published entry */ cache= find_cache_ent(arp_port, tpa); if (cache) { if (cache->ac_flags & ACF_PUB) { /* Published entry */ do_reply= 1; } else { /* Nothing to do */ cache= NULL; } } } if (ce == NULL) { if (!do_reply) return; DBLOCK(0x10, printf("arp[%d]: allocating entry for ", arp_port-arp_port_table); writeIpAddr(spa); printf("\n")); ce= alloc_cache_ent(ACF_EMPTY); ce->ac_flags= ACF_EMPTY; ce->ac_state= ACS_VALID; ce->ac_ethaddr= arp->a46_sha; ce->ac_ipaddr= spa; ce->ac_port= arp_port; ce->ac_expire= curr_time+ARP_EXP_TIME; ce->ac_lastuse= curr_time-ARP_INUSE_OFFSET; /* never used */ } if (ce->ac_state == ACS_INCOMPLETE || ce->ac_state == ACS_UNREACHABLE) { ce->ac_ethaddr= arp->a46_sha; if (ce->ac_state == ACS_INCOMPLETE) { /* Find request entry */ entry= ce-arp_cache; for (i= 0, reqp= arp_port->ap_req; i<AP_REQ_NR; i++, reqp++) { if (reqp->ar_entry == entry) break; } assert(i < AP_REQ_NR); clck_untimer(&reqp->ar_timer); reqp->ar_entry= -1; ce->ac_state= ACS_VALID; client_reply(arp_port, spa, &arp->a46_sha); } else ce->ac_state= ACS_VALID; } /* Update fields in the arp cache. */ if (memcmp(&ce->ac_ethaddr, &arp->a46_sha, sizeof(ce->ac_ethaddr)) != 0) { printf("arp[%d]: ethernet address for IP address ", arp_port-arp_port_table); writeIpAddr(spa); printf(" changed from "); writeEtherAddr(&ce->ac_ethaddr); printf(" to "); writeEtherAddr(&arp->a46_sha); printf("\n"); ce->ac_ethaddr= arp->a46_sha; } ce->ac_expire= curr_time+ARP_EXP_TIME; if (do_reply) { data= bf_memreq(sizeof(arp46_t)); arp= (arp46_t *)ptr2acc_data(data); /* Clear padding */ assert(sizeof(arp->a46_data.a46_dummy) % sizeof(*p) == 0); for (i= 0, p= (u16_t *)arp->a46_data.a46_dummy; i < sizeof(arp->a46_data.a46_dummy)/sizeof(*p); i++, p++) { *p= 0xdead; } arp->a46_dstaddr= ce->ac_ethaddr; arp->a46_hdr= HTONS(ARP_ETHERNET); arp->a46_pro= HTONS(ETH_IP_PROTO); arp->a46_hln= 6; arp->a46_pln= 4; arp->a46_op= htons(ARP_REPLY); if (tpa == arp_port->ap_ipaddr) { arp->a46_sha= arp_port->ap_ethaddr; } else { assert(cache); arp->a46_sha= cache->ac_ethaddr; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -