📄 eth.c
字号:
/*eth.cCopyright 1995 Philip Homburg*/#include "inet.h"#include "buf.h"#include "clock.h"#include "event.h"#include "osdep_eth.h"#include "type.h"#include "assert.h"#include "buf.h"#include "eth.h"#include "eth_int.h"#include "io.h"#include "sr.h"THIS_FILE#define ETH_FD_NR (4*IP_PORT_MAX)#define EXPIRE_TIME 60*HZ /* seconds */typedef struct eth_fd{ int ef_flags; nwio_ethopt_t ef_ethopt; eth_port_t *ef_port; struct eth_fd *ef_type_next; struct eth_fd *ef_send_next; int ef_srfd; acc_t *ef_rdbuf_head; acc_t *ef_rdbuf_tail; get_userdata_t ef_get_userdata; put_userdata_t ef_put_userdata; put_pkt_t ef_put_pkt; time_t ef_exp_time; size_t ef_write_count; ioreq_t ef_ioctl_req;} eth_fd_t;#define EFF_FLAGS 0xf# define EFF_EMPTY 0x0# define EFF_INUSE 0x1# define EFF_BUSY 0xE# define EFF_READ_IP 0x2# define EFF_WRITE_IP 0x4# define EFF_IOCTL_IP 0x8# define EFF_OPTSET 0x10/* Note that the vh_type field is normally considered part of the ethernet * header. */typedef struct { u16_t vh_type; u16_t vh_vlan;} vlan_hdr_t;FORWARD int eth_checkopt ARGS(( eth_fd_t *eth_fd ));FORWARD void hash_fd ARGS(( eth_fd_t *eth_fd ));FORWARD void unhash_fd ARGS(( eth_fd_t *eth_fd ));FORWARD void eth_buffree ARGS(( int priority ));#ifdef BUF_CONSISTENCY_CHECKFORWARD void eth_bufcheck ARGS(( void ));#endifFORWARD void packet2user ARGS(( eth_fd_t *fd, acc_t *pack, time_t exp_time ));FORWARD void reply_thr_get ARGS(( eth_fd_t *eth_fd, size_t result, int for_ioctl ));FORWARD void reply_thr_put ARGS(( eth_fd_t *eth_fd, size_t result, int for_ioctl ));FORWARD void do_rec_conf ARGS(( eth_port_t *eth_port ));FORWARD u32_t compute_rec_conf ARGS(( eth_port_t *eth_port ));FORWARD acc_t *insert_vlan_hdr ARGS(( eth_port_t *eth_port, acc_t *pack ));PUBLIC eth_port_t *eth_port_table;PUBLIC int no_ethWritePort= 0;PRIVATE eth_fd_t eth_fd_table[ETH_FD_NR];PRIVATE ether_addr_t broadcast= { { 255, 255, 255, 255, 255, 255 } };PUBLIC void eth_prep(){ eth_port_table= alloc(eth_conf_nr * sizeof(eth_port_table[0]));}PUBLIC void eth_init(){ int i, j; assert (BUF_S >= sizeof(nwio_ethopt_t)); assert (BUF_S >= ETH_HDR_SIZE); /* these are in fact static assertions, thus a good compiler doesn't generate any code for this */ for (i=0; i<ETH_FD_NR; i++) eth_fd_table[i].ef_flags= EFF_EMPTY; for (i=0; i<eth_conf_nr; i++) { eth_port_table[i].etp_flags= EPF_EMPTY; eth_port_table[i].etp_getstat= NULL; eth_port_table[i].etp_sendq_head= NULL; eth_port_table[i].etp_sendq_tail= NULL; eth_port_table[i].etp_type_any= NULL; ev_init(ð_port_table[i].etp_sendev); for (j= 0; j<ETH_TYPE_HASH_NR; j++) eth_port_table[i].etp_type[j]= NULL; for (j= 0; j<ETH_VLAN_HASH_NR; j++) eth_port_table[i].etp_vlan_tab[j]= NULL; }#ifndef BUF_CONSISTENCY_CHECK bf_logon(eth_buffree);#else bf_logon(eth_buffree, eth_bufcheck);#endif osdep_eth_init();}PUBLIC int eth_open(port, srfd, get_userdata, put_userdata, put_pkt, select_res)int port, srfd;get_userdata_t get_userdata;put_userdata_t put_userdata;put_pkt_t put_pkt;select_res_t select_res;{ int i; eth_port_t *eth_port; eth_fd_t *eth_fd; DBLOCK(0x20, printf("eth_open (%d, %d, %lx, %lx)\n", port, srfd, (unsigned long)get_userdata, (unsigned long)put_userdata)); eth_port= ð_port_table[port]; if (!(eth_port->etp_flags & EPF_ENABLED)) return EGENERIC; for (i=0; i<ETH_FD_NR && (eth_fd_table[i].ef_flags & EFF_INUSE); i++); if (i>=ETH_FD_NR) { DBLOCK(1, printf("out of fds\n")); return EAGAIN; } eth_fd= ð_fd_table[i]; eth_fd->ef_flags= EFF_INUSE; eth_fd->ef_ethopt.nweo_flags=NWEO_DEFAULT; eth_fd->ef_port= eth_port; eth_fd->ef_srfd= srfd; assert(eth_fd->ef_rdbuf_head == NULL); eth_fd->ef_get_userdata= get_userdata; eth_fd->ef_put_userdata= put_userdata; eth_fd->ef_put_pkt= put_pkt; return i;}PUBLIC int eth_ioctl(fd, req)int fd;ioreq_t req;{ acc_t *data; eth_fd_t *eth_fd; eth_port_t *eth_port; DBLOCK(0x20, printf("eth_ioctl (%d, 0x%lx)\n", fd, (unsigned long)req)); eth_fd= ð_fd_table[fd]; eth_port= eth_fd->ef_port; assert (eth_fd->ef_flags & EFF_INUSE); switch (req) { case NWIOSETHOPT: { nwio_ethopt_t *ethopt; nwio_ethopt_t oldopt, newopt; int result; u32_t new_en_flags, new_di_flags, old_en_flags, old_di_flags; data= (*eth_fd->ef_get_userdata)(eth_fd-> ef_srfd, 0, sizeof(nwio_ethopt_t), TRUE); ethopt= (nwio_ethopt_t *)ptr2acc_data(data); oldopt= eth_fd->ef_ethopt; newopt= *ethopt; old_en_flags= oldopt.nweo_flags & 0xffff; old_di_flags= (oldopt.nweo_flags >> 16) & 0xffff; new_en_flags= newopt.nweo_flags & 0xffff; new_di_flags= (newopt.nweo_flags >> 16) & 0xffff; if (new_en_flags & new_di_flags) { bf_afree(data); reply_thr_get (eth_fd, EBADMODE, TRUE); return NW_OK; } /* NWEO_ACC_MASK */ if (new_di_flags & NWEO_ACC_MASK) { bf_afree(data); reply_thr_get (eth_fd, EBADMODE, TRUE); return NW_OK; } /* you can't disable access modes */ if (!(new_en_flags & NWEO_ACC_MASK)) new_en_flags |= (old_en_flags & NWEO_ACC_MASK); /* NWEO_LOC_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_LOC_MASK)) { new_en_flags |= (old_en_flags & NWEO_LOC_MASK); new_di_flags |= (old_di_flags & NWEO_LOC_MASK); } /* NWEO_BROAD_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_BROAD_MASK)) { new_en_flags |= (old_en_flags & NWEO_BROAD_MASK); new_di_flags |= (old_di_flags & NWEO_BROAD_MASK); } /* NWEO_MULTI_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_MULTI_MASK)) { new_en_flags |= (old_en_flags & NWEO_MULTI_MASK); new_di_flags |= (old_di_flags & NWEO_MULTI_MASK); newopt.nweo_multi= oldopt.nweo_multi; } /* NWEO_PROMISC_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_PROMISC_MASK)) { new_en_flags |= (old_en_flags & NWEO_PROMISC_MASK); new_di_flags |= (old_di_flags & NWEO_PROMISC_MASK); } /* NWEO_REM_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_REM_MASK)) { new_en_flags |= (old_en_flags & NWEO_REM_MASK); new_di_flags |= (old_di_flags & NWEO_REM_MASK); newopt.nweo_rem= oldopt.nweo_rem; } /* NWEO_TYPE_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_TYPE_MASK)) { new_en_flags |= (old_en_flags & NWEO_TYPE_MASK); new_di_flags |= (old_di_flags & NWEO_TYPE_MASK); newopt.nweo_type= oldopt.nweo_type; } /* NWEO_RW_MASK */ if (!((new_en_flags | new_di_flags) & NWEO_RW_MASK)) { new_en_flags |= (old_en_flags & NWEO_RW_MASK); new_di_flags |= (old_di_flags & NWEO_RW_MASK); } if (eth_fd->ef_flags & EFF_OPTSET) unhash_fd(eth_fd); newopt.nweo_flags= ((unsigned long)new_di_flags << 16) | new_en_flags; eth_fd->ef_ethopt= newopt; result= eth_checkopt(eth_fd); if (result<0) eth_fd->ef_ethopt= oldopt; else { unsigned long opt_flags; unsigned changes; opt_flags= oldopt.nweo_flags ^ eth_fd->ef_ethopt.nweo_flags; changes= ((opt_flags >> 16) | opt_flags) & 0xffff; if (changes & (NWEO_BROAD_MASK | NWEO_MULTI_MASK | NWEO_PROMISC_MASK)) { do_rec_conf(eth_port); } } if (eth_fd->ef_flags & EFF_OPTSET) hash_fd(eth_fd); bf_afree(data); reply_thr_get (eth_fd, result, TRUE); return NW_OK; } case NWIOGETHOPT: { nwio_ethopt_t *ethopt; acc_t *acc; int result; acc= bf_memreq(sizeof(nwio_ethopt_t)); ethopt= (nwio_ethopt_t *)ptr2acc_data(acc); *ethopt= eth_fd->ef_ethopt; result= (*eth_fd->ef_put_userdata)(eth_fd-> ef_srfd, 0, acc, TRUE); if (result >= 0) reply_thr_put(eth_fd, NW_OK, TRUE); return result; } case NWIOGETHSTAT: { nwio_ethstat_t *ethstat; acc_t *acc; int result; assert (sizeof(nwio_ethstat_t) <= BUF_S); eth_port= eth_fd->ef_port; if (!(eth_port->etp_flags & EPF_ENABLED)) { reply_thr_put(eth_fd, EGENERIC, TRUE); return NW_OK; } if (!(eth_port->etp_flags & EPF_GOT_ADDR)) { printf( "eth_ioctl: suspending NWIOGETHSTAT ioctl\n"); eth_fd->ef_ioctl_req= req; assert(!(eth_fd->ef_flags & EFF_IOCTL_IP)); eth_fd->ef_flags |= EFF_IOCTL_IP; return NW_SUSPEND; } if (eth_port->etp_getstat) { printf( "eth_ioctl: pending eth_get_stat request, suspending caller\n"); assert(!(eth_fd->ef_flags & EFF_IOCTL_IP)); eth_fd->ef_flags |= EFF_IOCTL_IP; return NW_SUSPEND; } acc= bf_memreq(sizeof(nwio_ethstat_t)); compare (bf_bufsize(acc), ==, sizeof(*ethstat)); ethstat= (nwio_ethstat_t *)ptr2acc_data(acc); ethstat->nwes_addr= eth_port->etp_ethaddr; if (!eth_port->etp_vlan) { result= eth_get_stat(eth_port, ðstat->nwes_stat); if (result == SUSPEND) { printf( "eth_ioctl: eth_get_stat returned SUSPEND\n"); eth_fd->ef_ioctl_req= req; assert(!(eth_fd->ef_flags & EFF_IOCTL_IP)); eth_fd->ef_flags |= EFF_IOCTL_IP;printf("eth_ioctl: setting etp_getstat in port %d to %p\n", eth_port-eth_port_table, acc); eth_port->etp_getstat= acc; acc= NULL; return NW_SUSPEND; } if (result != NW_OK) { bf_afree(acc); reply_thr_put(eth_fd, result, TRUE); return NW_OK; } } else { /* No statistics */ memset(ðstat->nwes_stat, '\0', sizeof(ethstat->nwes_stat)); } result= (*eth_fd->ef_put_userdata)(eth_fd-> ef_srfd, 0, acc, TRUE); if (result >= 0) reply_thr_put(eth_fd, NW_OK, TRUE); return result; } default: break; } reply_thr_put(eth_fd, EBADIOCTL, TRUE); return NW_OK;}PUBLIC int eth_write(fd, count)int fd;size_t count;{ eth_fd_t *eth_fd; eth_port_t *eth_port, *rep; acc_t *user_data; int r; eth_fd= ð_fd_table[fd]; eth_port= eth_fd->ef_port; if (!(eth_fd->ef_flags & EFF_OPTSET)) { reply_thr_get (eth_fd, EBADMODE, FALSE); return NW_OK; } assert (!(eth_fd->ef_flags & EFF_WRITE_IP)); eth_fd->ef_write_count= count; if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY) count += ETH_HDR_SIZE; if (count<ETH_MIN_PACK_SIZE || count>ETH_MAX_PACK_SIZE) { DBLOCK(1, printf("illegal packetsize (%d)\n",count)); reply_thr_get (eth_fd, EPACKSIZE, FALSE); return NW_OK; } eth_fd->ef_flags |= EFF_WRITE_IP; /* Enqueue at the real ethernet port */ rep= eth_port->etp_vlan_port; if (!rep) rep= eth_port; if (rep->etp_wr_pack) { eth_fd->ef_send_next= NULL; if (rep->etp_sendq_head) rep->etp_sendq_tail->ef_send_next= eth_fd; else rep->etp_sendq_head= eth_fd; rep->etp_sendq_tail= eth_fd; return NW_SUSPEND; } user_data= (*eth_fd->ef_get_userdata)(eth_fd->ef_srfd, 0, eth_fd->ef_write_count, FALSE); if (!user_data) { eth_fd->ef_flags &= ~EFF_WRITE_IP; reply_thr_get (eth_fd, EFAULT, FALSE); return NW_OK; } r= eth_send(fd, user_data, eth_fd->ef_write_count); assert(r == NW_OK); eth_fd->ef_flags &= ~EFF_WRITE_IP; reply_thr_get(eth_fd, eth_fd->ef_write_count, FALSE); return NW_OK;}PUBLIC int eth_send(fd, data, data_len)int fd;acc_t *data;size_t data_len;{ eth_fd_t *eth_fd; eth_port_t *eth_port, *rep; eth_hdr_t *eth_hdr; acc_t *eth_pack; unsigned long nweo_flags; size_t count; ev_arg_t ev_arg; eth_fd= ð_fd_table[fd]; eth_port= eth_fd->ef_port; if (!(eth_fd->ef_flags & EFF_OPTSET)) return EBADMODE; count= data_len; if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY) count += ETH_HDR_SIZE; if (count<ETH_MIN_PACK_SIZE || count>ETH_MAX_PACK_SIZE) { DBLOCK(1, printf("illegal packetsize (%d)\n",count)); return EPACKSIZE; } rep= eth_port->etp_vlan_port; if (!rep) rep= eth_port; if (rep->etp_wr_pack) return NW_WOULDBLOCK; nweo_flags= eth_fd->ef_ethopt.nweo_flags; if (nweo_flags & NWEO_RWDATONLY) { eth_pack= bf_memreq(ETH_HDR_SIZE); eth_pack->acc_next= data; } else eth_pack= bf_packIffLess(data, ETH_HDR_SIZE); eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack); if (nweo_flags & NWEO_REMSPEC) eth_hdr->eh_dst= eth_fd->ef_ethopt.nweo_rem; if (!(eth_port->etp_flags & EPF_GOT_ADDR)) { /* No device, discard packet */ bf_afree(eth_pack); return NW_OK; } if (!(nweo_flags & NWEO_EN_PROMISC)) eth_hdr->eh_src= eth_port->etp_ethaddr; if (nweo_flags & NWEO_TYPESPEC) eth_hdr->eh_proto= eth_fd->ef_ethopt.nweo_type; if (eth_addrcmp(eth_hdr->eh_dst, eth_port->etp_ethaddr) == 0) { /* Local loopback. */ eth_port->etp_wr_pack= eth_pack; ev_arg.ev_ptr= eth_port; ev_enqueue(ð_port->etp_sendev, eth_loop_ev, ev_arg); return NW_OK; } if (rep != eth_port) { eth_pack= insert_vlan_hdr(eth_port, eth_pack); if (!eth_pack) { /* Packet is silently discarded */ return NW_OK; } } eth_write_port(rep, eth_pack); return NW_OK;}PUBLIC int eth_read (fd, count)int fd;size_t count;{ eth_fd_t *eth_fd; acc_t *pack; eth_fd= ð_fd_table[fd]; if (!(eth_fd->ef_flags & EFF_OPTSET)) { reply_thr_put(eth_fd, EBADMODE, FALSE); return NW_OK; } if (count < ETH_MAX_PACK_SIZE) { reply_thr_put(eth_fd, EPACKSIZE, FALSE); return NW_OK; } assert(!(eth_fd->ef_flags & EFF_READ_IP)); eth_fd->ef_flags |= EFF_READ_IP; while (eth_fd->ef_rdbuf_head) { pack= eth_fd->ef_rdbuf_head; eth_fd->ef_rdbuf_head= pack->acc_ext_link; if (get_time() <= eth_fd->ef_exp_time) { packet2user(eth_fd, pack, eth_fd->ef_exp_time); if (!(eth_fd->ef_flags & EFF_READ_IP)) return NW_OK; } else bf_afree(pack); } return NW_SUSPEND;}PUBLIC int eth_cancel(fd, which_operation)int fd;int which_operation;{ eth_fd_t *eth_fd, *prev, *loc_fd; eth_port_t *eth_port; DBLOCK(2, printf("eth_cancel (%d)\n", fd)); eth_fd= ð_fd_table[fd]; switch (which_operation) { case SR_CANCEL_READ: assert (eth_fd->ef_flags & EFF_READ_IP); eth_fd->ef_flags &= ~EFF_READ_IP; reply_thr_put(eth_fd, EINTR, FALSE); break; case SR_CANCEL_WRITE: assert (eth_fd->ef_flags & EFF_WRITE_IP); eth_fd->ef_flags &= ~EFF_WRITE_IP; /* Remove fd from send queue */ eth_port= eth_fd->ef_port; if (eth_port->etp_vlan_port) eth_port= eth_port->etp_vlan_port; for (prev= 0, loc_fd= eth_port->etp_sendq_head; loc_fd != NULL; prev= loc_fd, loc_fd= loc_fd->ef_send_next) { if (loc_fd == eth_fd) break; } assert(loc_fd == eth_fd); if (prev == NULL) eth_port->etp_sendq_head= loc_fd->ef_send_next; else prev->ef_send_next= loc_fd->ef_send_next; if (loc_fd->ef_send_next == NULL) eth_port->etp_sendq_tail= prev; reply_thr_get(eth_fd, EINTR, FALSE); break; case SR_CANCEL_IOCTL: assert (eth_fd->ef_flags & EFF_IOCTL_IP); eth_fd->ef_flags &= ~EFF_IOCTL_IP; reply_thr_get(eth_fd, EINTR, TRUE); break; default: ip_panic(( "got unknown cancel request" )); } return NW_OK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -