0025001 /*
0025002 eth.c
0025003
0025004 Copyright 1995 Philip Homburg
0025005 */
eth.c contains the operating system-independent portion of the ethernet code. Functions that open, close, and perform ioctl on ethernet file descriptors are found in eth.c. Furthermore, eth.c contains functions that route packets to the OS-dependent functions that send the packets to the ethernet task (driver) as well as functions that pass the packets to higher layers (e.g., ip).
0025006
0025007 #include "inet.h"
0025008 #include "buf.h"
0025009 #include "clock.h"
0025010 #include "event.h"
0025011 #include "osdep_eth.h"
0025012 #include "type.h"
0025013
0025014 #include "assert.h"
0025015 #include "buf.h"
0025016 #include "eth.h"
0025017 #include "eth_int.h"
0025018 #include "io.h"
0025019 #include "sr.h"
0025020
0025021 THIS_FILE
0025022
0025023 #define ETH_FD_NR (4*IP_PORT_MAX)
IP_PORT_MAX is #define'd in inet_config.h as 4 for an 80386. Therefore, there will be 16 ethernet file descriptors (i.e., ETH_FD_NR=16).
0025024 #define EXPIRE_TIME 60*HZ /* seconds */
0025025
0025026 typedef struct eth_fd
0025027 {
0025028 int ef_flags;
0025029 nwio_ethopt_t ef_ethopt;
0025030 eth_port_t *ef_port;
0025031 struct eth_fd *ef_type_next;
0025032 int ef_srfd;
0025033 acc_t *ef_rdbuf_head;
0025034 acc_t *ef_rdbuf_tail;
0025035 get_userdata_t ef_get_userdata;
0025036 put_userdata_t ef_put_userdata;
0025037 put_pkt_t ef_put_pkt;
0025038 time_t ef_exp_time;
0025039 size_t ef_write_count;
0025040 } eth_fd_t;
eth_fd_t / nwio_ethopt
If an ip port uses the ethernet data-link layer or an ethernet device file (e.g., /dev/eth) is opened directly, an ethernet file descriptor must be acquired. The relationships between various file descriptors and ports is shown in the following diagram:

typedef struct eth_fd
{
int ef_flags;
nwio_ethopt_t ef_ethopt;
eth_port_t *ef_port;
struct eth_fd *ef_type_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;
} eth_fd_t;
int ef_flags:
ef_flags can be the following:
#define EFF_FLAGS 0xf
#define EFF_EMPTY 0x0
#define EFF_INUSE 0x1
#define EFF_BUSY 0x6
#define EFF_READ_IP 0x2
#define EFF_WRITE_IP 0x4
#define EFF_OPTSET 0x8
ef_flags is initialized to EFF_EMPTY, but once the ethernet file descriptor is opened, ef_flags is set to EFF_INUSE. If the ethernet file descriptor has been configured and the nweo_flags (see below) are valid, the EFF_OPTSET (OPTions SET) flag is set. Only then can the ethernet file descriptor be used.
The EFF_BUSY flag is never set. The meaning of the other flags is self-explanatory.
nwio_ethopt_t ef_ethopt:
typedef struct nwio_ethopt
{
u32_t nweo_flags;
ether_addr_t nweo_multi, nweo_rem;
ether_type_t nweo_type;
} nwio_ethopt_t;
nweo_flags:
If the ethernet file descriptor was opened by the ip code, the following flags and type will be set:
nweo_flags= NWEO_COPY|NWEO_EN_BROAD|NWEO_EN_MULTI|NWEO_TYPESPEC;
nweo_type= HTONS(ETH_IP_PROTO);
#define NWEO_NOFLAGS 0x0000L
#define NWEO_ACC_MASK 0x0003L
#define NWEO_EXCL 0x00000001L
#define NWEO_SHARED 0x00000002L
#define NWEO_COPY 0x00000003L
From ip(4):
"If NWEO_SHARED is selected, then multiple channels (which all must select NWEO_SHARED) can use the same Ethernet type and they can all send packets. However, incoming packets will be delivered to at most one of them."
Note that, for whatever reason, NWEO_EXCL behaves exactly as NWEO_COPY. Every ethernet file descriptor so configured receives a copy of an incoming packet.
The access flags are important when an ethernet packet is being read.
#define NWEO_LOC_MASK 0x0010L
#define NWEO_BROAD_MASK 0x0020L
#define NWEO_EN_BROAD 0x00000020L
#define NWEO_DI_BROAD 0x00200000L
NWEO_EN_BROAD enables the receipt of broadcast packets.
#define NWEO_MULTI_MASK 0x0040L
#define NWEO_EN_MULTI 0x00000040L
#define NWEO_DI_MULTI 0x00400000L
NWEO_EN_MULTI enables the receipt of multicast packets. The nweo_multi field does not appear to be used in any meaningful way.
#define NWEO_PROMISC_MASK 0x0080L
#define NWEO_EN_PROMISC 0x00000080L
#define NWEO_DI_PROMISC 0x00800000L
If an ethernet file descriptor is in promiscuous mode, the file descriptor not only accepts any packet regardless of destination ethernet address but can also send out packets with any source ethernet address (not just the ethernet card's address).
If this is not the case, use the ethernet port's ethernet address.
#define NWEO_REM_MASK 0x0100L
#define NWEO_REMSPEC 0x00000100L
#define NWEO_REMANY 0x01000000L
From ip(4):
"NWEO_REMSPEC restricts sending and receiving of packets to the single remote computer specified in the nweo_rem field."
If the NWEO_REMANY flag is set, an ethernet packet may have any destination.
#define NWEO_TYPE_MASK 0x0200L
#define NWEO_TYPESPEC 0x00000200L
#define NWEO_TYPEANY 0x02000000L
From ip(4):
"NWEO_TYPESPEC restricts sending and receiving of packets to the type specified in nweo_type."
If the NWEO_TYPESPEC flag is set, the nweo_type field (see below) may be one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
#define NWEO_RW_MASK 0x1000L
#define NWEO_RWDATONLY 0x00001000L
#define NWEO_RWDATALL 0x10000000L
From ip(4):
"If the Ethernet header is completely specified by the nweo_flags (i.e.,
all of NWEO_EN_LOC, NWEO_DI_BROAD, NWEO_DI_MULTI, NWEO_DI_PROMISC,
NWEO_REMSPEC and NWEO_TYPESPEC are specified), then NWEO_RWDATONLY can be
used to send and receive only the data part of an Ethernet packet."
The default for the ethernet file descriptors opened by the ip and arp layers is NWEO_RWDATALL.
ether_addr_t nweo_multi:
This field is not used in any meaningful way.
nweo_rem:
Used with the NWEO_REMSPEC flag (see above).
ether_type_t nweo_type:
Used with the NWEO_TYPESPEC flag (see above). The nweo_type field may be one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
eth_port_t *ef_port:
All ethernet file descriptors have an associated ethernet port (see figure above). ef_port is this associated ethernet port.
struct eth_fd *ef_type_next:
Two linked lists (actually, one linked list and one array of linked lists) of ethernet file descriptors in an ethernet port are etp_type_any and etp_type[]. ef_type_next links the ethernet file descriptors in these two linked lists.
int ef_srfd:
If the ip layer opens up this ethernet file descriptor, then ef_srfd will be the ip port that corresponds to this ethernet file descriptor. If the ethernet file descriptor is opened up directly (and eth_open() is called indirectly), then ef_srfd will be the corresponding slot in sr_fd_table[].
acc_t *ef_rdbuf_head; acc_t *ef_rdbuf_tail:
Incoming packets are placed in the ef_rdbuf_head/ef_rdbuf_tail linked list if the ethernet file descriptor is not being read (i.e., the EFF_READ_IP flag is not set).
get_userdata_t ef_get_userdata; put_userdata_t ef_put_userdata:
If the ip layer opens up this ethernet file descriptor, then ef_get_userdata will be get_eth_data() and ef_put_userdata will be put_eth_data(). If the ethernet file descriptor is opened up directly (and eth_open() is called indirectly), then ef_get_userdata will be set to sr_get_userdata (which copies data from a user process to a buffer in the network process) and ef_put_userdata will be set to sr_put_userdata (which copies data from a buffer in the network process to a user process).
put_pkt_t ef_put_pkt:
ef_put_pkt is set to ip_eth_arrived() by eth_open(). ip_eth_arrived() moves a packet from the ethernet layer to the ip layer.
time_t ef_exp_time:
ef_exp_time is the expiration timer for the read queue. If the ethernet file descriptor's read queue is empty and packet2user() was not able to pass a packet that arrived from the ethernet task to the higher layer (e.g., the ip code), packet2user() places the packet in the read queue and sets the timer. Note that there is only a single timer for the read queue so if the timer has expired for the first packet, all of the packets will be discarded.
size_t ef_write_count:
ef_write_count is the size (in bytes) of the next packet to be sent out the ethernet file descriptor. If an ip port is associated with the ethernet file descriptor, this packet is in the ip port's de_frame field.
0025041
0025042 #define EFF_FLAGS 0xf
0025043 # define EFF_EMPTY 0x0
0025044 # define EFF_INUSE 0x1
0025045 # define EFF_BUSY 0x6
0025046 # define EFF_READ_IP 0x2
0025047 # define EFF_WRITE_IP 0x4
0025048 # define EFF_OPTSET 0x8
0025049
0025050 FORWARD int eth_checkopt ARGS(( eth_fd_t *eth_fd ));
0025051 FORWARD void hash_fd ARGS(( eth_fd_t *eth_fd ));
0025052 FORWARD void unhash_fd ARGS(( eth_fd_t *eth_fd ));
0025053 FORWARD void eth_buffree ARGS(( int priority ));
0025054 #ifdef BUF_CONSISTENCY_CHECK
0025055 FORWARD void eth_bufcheck ARGS(( void ));
0025056 #endif
0025057 FORWARD void packet2user ARGS(( eth_fd_t *fd, acc_t *pack, time_t exp_time ));
0025058 FORWARD void reply_thr_get ARGS(( eth_fd_t *eth_fd,
0025059 size_t result, int for_ioctl ));
0025060 FORWARD void reply_thr_put ARGS(( eth_fd_t *eth_fd,
0025061 size_t result, int for_ioctl ));
0025062 FORWARD u32_t compute_rec_conf ARGS(( eth_port_t *eth_port ));
0025063
0025064 PUBLIC eth_port_t *eth_port_table;
Also, a good understanding of ethernet ports is necessary.
eth_port_t / osdep_eth_port_t
Each ethernet card on a system will have one eth_port_t associated with it. In other words, if there is only a single ethernet card on a system, eth_port_table[] will have only a single element. eth_port_t minus the osdep_eth_port_t field is the OS independent portion of the ethernet configuration.
typedef struct eth_port
{
int etp_flags;
ether_addr_t etp_ethaddr;
acc_t *etp_wr_pack, *etp_rd_pack;
struct eth_fd *etp_type_any;
struct eth_fd *etp_type[ETH_TYPE_HASH_NR];
event_t etp_sendev;
osdep_eth_port_t etp_osdep;
} eth_port_t;
The OS dependent (= osdep) part of the ethernet port configuration is osdep_eth_port_t. In other words, if you were to port this network service to another OS, you'd need to create an osdep_eth_port_t that matched your OS.
typedef struct osdep_eth_port
{
int etp_task;
int etp_port;
int etp_recvconf;
iovec_t etp_wr_iovec[IOVEC_NR];
iovec_t etp_rd_iovec[RD_IOVEC];
event_t etp_recvev;
message etp_sendrepl;
message etp_recvrepl;
} osdep_eth_port_t;
int etp_flags:
Initialized to EPF_ENABLED in osdep_eth_init(). In setup_read(), if data has not been received by the ethernet task, etp_flags is set to EPF_READ_IP (although I don't know why yet. If the ethernet task has not delivered a packet yet and another packet is also waiting to be delivered, the EPF_MORE2WRITE flag is set.
ether_addr_t etp_ethaddr:
The ethernet address of the port. This will be a 6 octet number like 00-E0-81-00-29-F6 (the ethernet address of my system). The address of the ethernet port is obtained by querying the ethernet task and disassembling its response. This ethernet address is generally used as the source address for outgoing packets. However, if an ethernet file descriptor is in promiscuous mode, the file descriptor not only accepts any packet regardless of destination ethernet address but can also send out packets with any source ethernet address (not just the ethernet card's address).
typedef struct ether_addr
{
u8_t ea_addr[6];
} ether_addr_t;
acc_t *etp_wr_pack:
The queueing for ethernet packets being sent out by the ethernet task is somewhat convoluted. If no ethernet packets are waiting to be sent out by the ethernet task (driver), eth_write_port() stores an ethernet packet in the etp_wr_pack field until the packet is sent off by the ethernet task. After the ethernet task successfully sends the packet off, this field is set to NULL (either by eth_write_port() or write_int(). If the ethernet task cannot immediately send the ethernet packet off, the packet remains in etp_wr_pack. If another packet arrives for an ip port to send off to the ethernet port, the ip port encapsulates the ip packet and the resulting ethernet packet is placed in the dl_eth.de_frame field of the ip port. If an ip port then has additional packets that it wishes to send out, the packets are placed in the dl_eth.de_q_head/dl_eth.de_q_tail queue until the ethernet packets in etp_wr_pack and dl_eth.de_frame are sent out.
It's important to note that neither etp_wr_pack nor dl_eth.de_frame are linked lists (i.e., queues). They each hold only a single ethernet packet.
acc_t *etp_rd_pack:
The read queue for the ethernet port. If a read request message was sent to the ethernet driver and an ethernet packet was not already received, this is an empty buffer waiting to be filled by a packet received later.
struct eth_fd *etp_type_any:
struct eth_fd *etp_type[ETH_TYPE_HASH_NR]:
When an ethernet file descriptor is configured, either the NWEO_TYPESPEC flag or the NWEO_TYPEANY flag is set for the ef_flags field of the ethernet file descriptor (eth_fd). If NWEO_TYPEANY is set, any type of packet is accepted by the file descriptor. If NWEO_TYPESPEC if set, nweo_type is set to one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
After this configuration is done, hash_fd() is called to place the file descriptor in either the etp_type_any linked list (if the NWEO_TYPEANY flag is set) or in one of the linked lists in etp_type[] (there is a single linked list for each of the types shown above). The figure below shows an example of an etp_type[] linked list with three ethernet file descriptors.

event_t etp_sendev:
After sending out an ethernet packet for an ethernet port, the ethernet driver sends a message back to the ethernet port indicating completion. etp_sendev is the event queue that contains the notification event in case the message could not be immediately delivered.
int etp_task:
etp_task is determined by calling sys_findproc() and passing in the name of the task. In the following same configuration:
eth0 DP8390 0 { default; };
psip1;
the task would be "DP8390" for the ethernet device.
int etp_port:
The port number of the ethernet device as determined by the configuration file. For the following inet.conf file for a system with two DP8390 ethernet cards:
eth0 DP8390 0 { default; };
psip1;
eth2 DP8390 1;
the ethernet port associated with eth0 will have an etp_port field equal to 0 and the ethernet port associated with eth2 will have an etp_port field equal to 1.
int etp_recvconf:
etp_recvconf is the "receive configuration" and is set by eth_set_rec_conf(). The receive configuration reflects whether an ethernet port is receiving broadcast and multicast packets and whether the port is in promiscuous mode.
iovec_t etp_wr_iovec[IOVEC_NR]:
iovec_t etp_rd_iovec[RD_IOVEC]:
The buffers that the ethernet code uses to write/read data to/from the ethernet device. For example, on lines 10419-10438 of setup_read(), the message to the ethernet task requesting any data read by the task is set up and sent.
event_t etp_recvev:
etp_recvev is the event queue for the ethernet port.
message etp_sendrepl, message etp_recvrepl:
When a message arrives from the ethernet driver indicating that a packet has been sent or received and the message cannot be immediately processed, the message is placed here. An event (with function eth_recvev() or eth_sendev()) is also added to the event queue. When an event for a received packet is processed, eth_recvev() determines from the DL_COUNT field how many bytes have been received. If the event is for a completed write operation, the fields in the message are not of any significance.
0025065
0025066 PRIVATE eth_fd_t eth_fd_table[ETH_FD_NR];
As mentioned on line 25023, ETH_FD_NR is 16 (for 80386 and up) and so there will be 16 elements in the table of ethernet file descriptors (i.e., eth_fd_table[]).
0025067 PRIVATE ether_addr_t broadcast= { { 255, 255, 255, 255, 255, 255 } };
0025068
0025069 PUBLIC void eth_prep()
eth_prep()
eth_prep() simply allocates memory for the ethernet port table. The ethernet ports (as well as the ethernet file descriptors) are later initialized by eth_init().
0025070 {
0025071 eth_port_table= alloc(eth_conf_nr * sizeof(eth_port_table[0]));
eth_conf_nr was set in read_conf().
For the following inet.conf file:
eth0 DP8390 0 { default; };
psip1;
eth_conf_nr will be 1 since there is only one ethernet interface. eth_port_table[] will therefore consist of a single element which corresponds to our single ethernet interface.
0025072 }
0025073
0025074 PUBLIC void eth_init()
eth_init()
eth_init() initializes the ethernet file descriptors and then the operating system-independent fields of the ethernet ports before calling osdep_eth_init() to initialize the operating system-dependent fields of the ethernet ports.
0025075 {
0025076 int i, j;
0025077
0025078 assert (BUF_S >= sizeof(nwio_ethopt_t));
0025079 assert (BUF_S >= ETH_HDR_SIZE); /* these are in fact static assertions,
0025080 thus a good compiler doesn't
0025081 generate any code for this */
0025082
0025083 #if ZERO
Initialize the ethernet file descriptors.
ZERO is #define'd in inet.h as 0. ZERO is used to comment out initialization code that does nothing. EFF_EMPTY is 0 and NULL is (naturally) also zero. Since these fields were zero before this function was called, setting the fields to zero does nothing.
On line 25088, EFF_EMPTY is used. It would appear that EPF_EMPTY should have been used. However, since both #define's are zero (and the block isn't executed anyway), it doesn't matter.
0025084 for (i=0; i<ETH_FD_NR; i++)
0025085 eth_fd_table[i].ef_flags= EFF_EMPTY;
0025086 for (i=0; i<eth_conf_nr; i++)
Initialize the operating system-independent fields of the ethernet ports.
For our example inet.conf file, eth_conf_nr is only 1 so this loop will execute only once.
0025087 {
0025088 eth_port_table[i].etp_flags= EFF_EMPTY;
0025089 eth_port_table[i].etp_type_any= NULL;
0025090 ev_init(ð_port_table[i].etp_sendev);
event_t / ev_enqueue() / ev_process() / ev_init() / ev_in_queue()
The event_t typedef is declared in inet/generic/event.h:
typedef struct event
{
ev_func_t ev_func;
ev_arg_t ev_arg;
struct event *ev_next;
} event_t;
If an event needs to be scheduled, ev_enqueue() is called to place the event in the system-wide event queue whose head is ev_head. ev_process() is eventually called from the main loop in inet.c to process the events. ev_in_queue(ev) simply returns TRUE if the event ev, ev_in_queue()'s only parameter, has a non-null value for func (see below) and FALSE if func is null. In this way, ev_in_queue() determines whether the event has been configured.
ev_init(ev) simply zeroes out the ev_func and ev_next fields of the event ev, ev_init()'s only parameter.
ev_func: A function (e.g., ip_process_loopb()) that performs some task.
ev_arg:
typedef union ev_arg
{
int ev_int;
void *ev_ptr;
} ev_arg_t;
ev_arg is ev_func's argument. In the case of a packet destined for the loopback address (127.0.0.1), the argument will be the ip port associated with the ip file descriptor that is sending out the packet. In the case of a message from the ethernet task that caused a deadlock, ev_arg is a pointer to the message's destination ethernet port.
ev_next: The next event in the system-wide event queue.
0025091 for (j= 0; j<ETH_TYPE_HASH_NR; j++)
0025092 eth_port_table[i].etp_type[j]= NULL;
0025093 }
0025094 #endif
0025095
0025096 #ifndef BUF_CONSISTENCY_CHECK
0025097 bf_logon(eth_buffree);
bf_logon()
bf_logon() is used by eth_init(), psip_init(), ip_init(), icmp_init(), tcp_init(), and udp_init() to register their functions for freeing buffers. For example, eth_init() calls bf_logon() with an argument of eth_buffree().
After bf_logon() is finished, freereq[] is configured as follows:
freereq[0]=eth_buffree
freereq[1]=psip_buffree
freereq[2]=ip_buffree
freereq[3]=icmp_buffree
freereq[4]=tcp_buffree
freereq[5]=udp_buffree
0025098 #else
0025099 bf_logon(eth_buffree, eth_bufcheck);
0025100 #endif
0025101
0025102 osdep_eth_init();
osdep_eth_init()
osdep_eth_init() ("osdep" stands for "OS DEPendent") is called by eth_init() during the initialization of the network service. osdep_eth_init() calls several Minix-specific functions to initialize several Minix-independent and Minix-dependent ethernet port fields (for example, the ethernet address is obtained by querying from the ethernet driver).
After each ethernet port is configured, sr_add_minor() and setup_read() are called for the port.
0025103 }
0025104
0025105 PUBLIC int eth_open(port, srfd, get_userdata, put_userdata, put_pkt)
0025106 int port, srfd;
0025107 get_userdata_t get_userdata;
0025108 put_userdata_t put_userdata;
0025109 put_pkt_t put_pkt;
eth_open()
eth_open(port, srfd, get_userdata, put_userdata, put_pkt) finds an ethernet file descriptor that is free and associates the file descriptor with an ethernet port whose index within eth_port_table[] is port, eth_open()'s first parameter.
eth_open() is called by the ip code, the arp code and is called if an ethernet device file (e.g., /dev/eth) is opened directly.
Here are the relationships between various file descriptors and ports:

0025110 {
0025111 int i;
0025112 eth_port_t *eth_port;
0025113 eth_fd_t *eth_fd;
0025114
0025115 DBLOCK(0x20, printf("eth_open (%d, %d, %lx, %lx)\n", port, srfd,
0025116 (unsigned long)get_userdata, (unsigned long)put_userdata));
0025117 eth_port= ð_port_table[port];
Find the ethernet file descriptor whose index within eth_port_table[] is port, eth_open()'s first parameter.
0025118 if (!(eth_port->etp_flags & EPF_ENABLED))
0025119 return EGENERIC;
0025120
0025121 for (i=0; i<ETH_FD_NR && (eth_fd_table[i].ef_flags & EFF_INUSE);
0025122 i++);
Find the first unused entry in eth_fd_table[]. If there are no available ethernet file descriptors, return on line 25127.
0025123
0025124 if (i>=ETH_FD_NR)
0025125 {
0025126 DBLOCK(1, printf("out of fds\n"));
0025127 return EAGAIN;
0025128 }
0025129
0025130 eth_fd= ð_fd_table[i];
0025131
0025132 eth_fd->ef_flags= EFF_INUSE;
0025133 eth_fd->ef_ethopt.nweo_flags=NWEO_DEFAULT;
0025134 eth_fd->ef_port= eth_port;
Every ethernet file descriptor is linked to a single ethernet port and each ethernet port is linked to multiple ethernet file descriptors.
0025135 eth_fd->ef_srfd= srfd;
0025136 assert(eth_fd->ef_rdbuf_head == NULL);
0025137 eth_fd->ef_get_userdata= get_userdata;
0025138 eth_fd->ef_put_userdata= put_userdata;
0025139 eth_fd->ef_put_pkt= put_pkt;
If the ethernet file descriptor is opened by the ip code, these three fields are set to get_eth_data(), put_eth_data(), and ip_eth_arrived(), respectively.
0025140 return i;
0025141 }
0025142
0025143 PUBLIC int eth_ioctl(fd, req)
0025144 int fd;
0025145 ioreq_t req;
eth_ioctl()
The actions of eth_ioctl(fd, req) depend on req, eth_ioctl()'s second parameter:
NWIOSETHOPT (NetWork IO Set ETHernet OPTions):
If req is NWIOSETHOPT, eth_ioctl() configures the ethernet file descriptor fd (eth_ioctl()'s first parameter), which can then be used by a higher layer (e.g., ip, arp).
NWIOGETHSTAT (NetWork IO Get ETHernet STATs):
Only the arp code calls eth_ioctl() with the second parameter set to NWIOGETHSTAT. In this case, the ap_ethaddr field of the arp port is set to the ethernet address of the ethernet file descriptor's underlying ethernet port.
0025146 {
0025147 acc_t *data;
0025148 eth_fd_t *eth_fd;
0025149 eth_port_t *eth_port;
0025150
0025151 DBLOCK(0x20, printf("eth_ioctl (%d, %lu)\n", fd, req));
Find the ethernet file descriptor whose index within eth_fd_table[] is fd, eth_ioctl()'s first parameter and find its associated ethernet port.
0025152 eth_fd= ð_fd_table[fd];
0025153 eth_port= eth_fd->ef_port;
0025154
0025155 assert (eth_fd->ef_flags & EFF_INUSE);
0025156
0025157 switch (req)
0025158 {
0025159 case NWIOSETHOPT:
When the network service first starts up, the state of the underlying ethernet file descriptor of a newly created ip port (if it is associated with an ethernet file descriptor and not a psip file descriptor) is IES_EMPTY. During the configuration of this underlying ethernet file descriptor, eth_ioctl() is called by ipeth_main() with a second argument of NWIOSETHOPT.
eth_ioctl(), when called with a second argument of NWIOSETHOPT, gets the configuration data from the higher-layer (e.g., ip, arp), checks whether the data is acceptable, and then sets the ef_ethopt field of the ethernet file descriptor to reflect the request.
0025160 {
0025161 nwio_ethopt_t *ethopt;
0025162 nwio_ethopt_t oldopt, newopt;
0025163 int result;
0025164 u32_t new_en_flags, new_di_flags,
0025165 old_en_flags, old_di_flags;
0025166 u32_t flags;
0025167
0025168 data= (*eth_fd->ef_get_userdata)(eth_fd->
0025169 ef_srfd, 0, sizeof(nwio_ethopt_t), TRUE);
If the ip code opened this ethernet file descriptor, the ef_get_userdata field is set to get_eth_data(). If this is the case, get_eth_data() will here return an nwio_ethopt struct with the following flags and type set:
nweo_flags= NWEO_COPY|NWEO_EN_BROAD|NWEO_EN_MULTI|NWEO_TYPESPEC;
nweo_type= HTONS(ETH_IP_PROTO);
If the ethernet file descriptor was opened by the arp code, the nwio_ethopt struct will have the following flags and type:
nweo_flags= NWEO_COPY|NWEO_EN_BROAD|NWEO_TYPESPEC;
nweo_type= HTONS(ETH_ARP_PROTO);
get_eth_data()
get_eth_data(fd, offset, count, for_ioctl) is (indirectly) called by a number of functions within the ethernet code, including eth_write(). get_eth_data() performs one of several tasks, depending on the state of the ip port and the value of count, get_eth_data()'s third parameter.
If the state of the ip port is IES_MAIN (its state during normal operations) and count is nonzero, get_eth_data() returns the packet from the de_frame field of the ip port. In this way, eth_write() gets the packet from the ip code to send off to eth_send().
If count is zero, get_eth_data() does something different. After eth_write() calls eth_send() (and the ethernet frame is therefore delivered), eth_write() calls the ethernet's reply_thr_get() with count equal to zero. If the ethernet file descriptor was opened up by the ip code, reply_thr_get() is simply a wrapper for eth_get_data(). In this scenario, get_eth_data() sets the ip port's de_frame field to null (since eth_send() just passed this packet to the ethernet driver) and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ip port's state is IES_PROTO (its configuration state), get_user_data() handles an initialization-related task. If count, get_eth_data()'s third parameter, is not zero (0), get_eth_data() sets various fields of an nwio_ethopt struct appropriate for the ip protocol and then returns a pointer to the struct.
When ipeth_main() calls eth_ioctl() the first time, eth_ioctl() in turn (indirectly) calls eth_get_data() to get the nwio_ethopt struct constructed by eth_get_data().
If count is zero and the ip port's state is IES_PROTO, get_eth_data() calls ipeth_main() if additional initialization is necessary.
arp_getdata()
arp_getdata(fd, offset, count, for_ioctl) accomplishes several different tasks, depending on the state of the arp port:
APS_ARPPROTO (configuration state):
If count, arp_getdata()'s third parameter, is non-zero, arp_getdata() creates a nwio_eth_opt struct and sets the appropriate flags and type for an ethernet file descriptor that services an arp port:
nweo_flags= NWEO_COPY|NWEO_EN_BROAD|NWEO_TYPESPEC;
nweo_type= HTONS(ETH_ARP_PROTO);
and then returns an accessor that contains the struct.
ARS_ARPMAIN (operational state):
If count, arp_getdata()'s third parameter, is non-zero, arp_getdata() creates an arp packet (an arp46_t struct) and returns the struct. eth_write() calls arp_getdata() to create an arp-request or arp_reply packet (the packets that build the arp table. The ip and ethernet address of the arp-reply or request is gotten from the ap_write_ipaddr and ap_write_ethaddr fields, respectively, of the arp port.
0025170
0025171 ethopt= (nwio_ethopt_t *)ptr2acc_data(data);
ptr2acc_data()
The macro ptr2acc_data is #define'd in inet/generic/buf.h as:
#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))
ptr2acc_data() simply returns a pointer to the actual data within an accessor.
ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.
0025172 oldopt= eth_fd->ef_ethopt;
0025173 newopt= *ethopt;
0025174
The low 2 bytes of nweo_flags are the "enable" flags and the high 2 bytes are the "disable" flags. These flags are analyzed in lines 25179-25258.
0025175 old_en_flags= oldopt.nweo_flags & 0xffff;
0025176 old_di_flags= (oldopt.nweo_flags >> 16) & 0xffff;
0025177 new_en_flags= newopt.nweo_flags & 0xffff;
0025178 new_di_flags= (newopt.nweo_flags >> 16) & 0xffff;
0025179 if (new_en_flags & new_di_flags)
0025180 {
0025181 bf_afree(data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025182 reply_thr_get (eth_fd, EBADMODE, TRUE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025183 return NW_OK;
0025184 }
0025185
Unlike the other sets of flags, the enable access flags do not have disable counterparts. The access modes are as follows:
define NWEO_ACC_MASK 0x0003L
#define NWEO_EXCL 0x00000001L
#define NWEO_SHARED 0x00000002L
#define NWEO_COPY 0x00000003L
If the ethernet file descriptor is opened by the ip code, the default access mode will be NWEO_COPY.
For a detailed description of the access modes, click here.
0025186 /* NWEO_ACC_MASK */
0025187 if (new_di_flags & NWEO_ACC_MASK)
0025188 {
0025189 bf_afree(data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025190 reply_thr_get (eth_fd, EBADMODE, TRUE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025191 return NW_OK;
0025192 }
0025193 /* you can't disable access modes */
0025194
0025195 if (!(new_en_flags & NWEO_ACC_MASK))
0025196 new_en_flags |= (old_en_flags & NWEO_ACC_MASK);
0025197
0025198
For lines 25199-25249, if the options obtained on lines 25168-25169 contain neither an enable nor a disable flag for a given set of flags, continue to use the current options.
0025199 /* NWEO_LOC_MASK */
0025200 if (!((new_en_flags | new_di_flags) & NWEO_LOC_MASK))
0025201 {
0025202 new_en_flags |= (old_en_flags & NWEO_LOC_MASK);
0025203 new_di_flags |= (old_di_flags & NWEO_LOC_MASK);
0025204 }
0025205
0025206 /* NWEO_BROAD_MASK */
0025207 if (!((new_en_flags | new_di_flags) & NWEO_BROAD_MASK))
0025208 {
0025209 new_en_flags |= (old_en_flags & NWEO_BROAD_MASK);
0025210 new_di_flags |= (old_di_flags & NWEO_BROAD_MASK);
0025211 }
0025212
0025213 /* NWEO_MULTI_MASK */
0025214 if (!((new_en_flags | new_di_flags) & NWEO_MULTI_MASK))
0025215 {
0025216 new_en_flags |= (old_en_flags & NWEO_MULTI_MASK);
0025217 new_di_flags |= (old_di_flags & NWEO_MULTI_MASK);
0025218 newopt.nweo_multi= oldopt.nweo_multi;
0025219 }
0025220
0025221 /* NWEO_PROMISC_MASK */
0025222 if (!((new_en_flags | new_di_flags) & NWEO_PROMISC_MASK))
0025223 {
0025224 new_en_flags |= (old_en_flags & NWEO_PROMISC_MASK);
0025225 new_di_flags |= (old_di_flags & NWEO_PROMISC_MASK);
0025226 }
0025227
0025228 /* NWEO_REM_MASK */
0025229 if (!((new_en_flags | new_di_flags) & NWEO_REM_MASK))
0025230 {
0025231 new_en_flags |= (old_en_flags & NWEO_REM_MASK);
0025232 new_di_flags |= (old_di_flags & NWEO_REM_MASK);
0025233 newopt.nweo_rem= oldopt.nweo_rem;
0025234 }
0025235
0025236 /* NWEO_TYPE_MASK */
0025237 if (!((new_en_flags | new_di_flags) & NWEO_TYPE_MASK))
0025238 {
0025239 new_en_flags |= (old_en_flags & NWEO_TYPE_MASK);
0025240 new_di_flags |= (old_di_flags & NWEO_TYPE_MASK);
0025241 newopt.nweo_type= oldopt.nweo_type;
0025242 }
0025243
0025244 /* NWEO_RW_MASK */
0025245 if (!((new_en_flags | new_di_flags) & NWEO_RW_MASK))
0025246 {
0025247 new_en_flags |= (old_en_flags & NWEO_RW_MASK);
0025248 new_di_flags |= (old_di_flags & NWEO_RW_MASK);
0025249 }
0025250
0025251 if (eth_fd->ef_flags & EFF_OPTSET)
0025252 unhash_fd(eth_fd);
If the EFF_OPTSET flag for the ethernet file descriptor is set, the file descriptor was previously configured and placed in a linked list of file descriptors. Remove it from the linked list. It will be added to the appropriate linked list later.
hash_fd() / unhash_fd()
When an ethernet file descriptor is configured, either the NWEO_TYPESPEC flag or the NWEO_TYPEANY flag is set for the ef_flags field of the ethernet file descriptor eth_fd, the only parameter of both hash_fd() and unhash_fd(). If NWEO_TYPEANY is set, any type of packet is accepted by the file descriptor. If NWEO_TYPESPEC if set, the nweo_type field of eth_fd is set to one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
After this configuration is done, hash_fd(eth_fd) is called to place the file descriptor in either the etp_type_any linked list (if the NWEO_TYPEANY flag is set) or in one of the linked lists in etp_type[] (there is a single linked list for each of the types shown above). Placing the file descriptors in these linked lists decreases the time needed to later find a file descriptor.
The figure below shows an example of an etp_type[] linked list with three ethernet file descriptors.

unhash_fd(eth_fd) removes the ethernet file descriptor from this linked list.
0025253
0025254 newopt.nweo_flags= ((unsigned long)new_di_flags << 16) |
0025255 new_en_flags;
0025256 eth_fd->ef_ethopt= newopt;
0025257
0025258 result= eth_checkopt(eth_fd);
eth_checkopt()
eth_checkopt() verifies that an enable or disable flag for each set of flags (e.g., NWEO_ACC_MASK) is set. If this is the case, the EFF_OPTSET flag for the ethernet file descriptor is set and therefore the file descriptor can be used (e.g., written to/read from).
In addition to the verification of the flags, discard any packets in the read queue of the ethernet file descriptor. Since the options of the ethernet file descriptor are being changed, it is likely that the packets are no longer appropriate for this file descriptor.
0025259
0025260 if (result<0)
0025261 eth_fd->ef_ethopt= oldopt;
0025262 else
0025263 {
0025264 unsigned long opt_flags;
0025265 unsigned changes;
0025266 opt_flags= oldopt.nweo_flags ^
0025267 eth_fd->ef_ethopt.nweo_flags;
^ is the exclusive OR operator. For example, 0110 ^ 1010 = 1100.
If any of the flags have changed, change will be nonzero.
0025268 changes= ((opt_flags >> 16) | opt_flags) &
0025269 0xffff;
0025270 if (changes & (NWEO_BROAD_MASK |
0025271 NWEO_MULTI_MASK | NWEO_PROMISC_MASK))
If the broadcast, multicast or promiscuous mode flag has changed, a message must be sent to the ethernet task indicating the change.
0025272 {
0025273 flags= compute_rec_conf(eth_port);
compute_rec_conf()
compute_rec_conf() OR's the nweo_flag field for all of the configured ethernet file descriptors for a given ethernet port and returns the results. So, for example, if at least one of the ethernet file descriptors have the NWEO_PROMISC_MASK flag set, the result returned will reflect this.
0025274 eth_set_rec_conf(eth_port, flags);
eth_set_rec_conf ()
eth_set_rec_conf() sends a message requesting the ethernet task to change the receiving capabilities for an ethernet port. For example, if an ethernet file descriptor is being configured to receive broadcasts, eth_set_rec_conf() will send a message to the ethernet task requesting that broadcast packets for the port be accepted.
Note that eth_set_rec_conf() is called by eth_ioctl() for a NWIOSETHOPT (NetWork IO Set ETHernet OPTions) request. Since only root can open and configure the device file /dev/eth, only root can configure an ethernet file descriptor and, therefore, only root is able to place the ethernet card in promiscuous mode.
0025275 }
0025276 }
0025277
0025278 if (eth_fd->ef_flags & EFF_OPTSET)
0025279 hash_fd(eth_fd);
eth_checkopt() previously (on line 25258) set EFF_OPTSET if it found the ethernet file descriptor's options to be acceptable. If so, place the file descriptor in the appropriate linked list of the associated ethernet port.
hash_fd() / unhash_fd()
When an ethernet file descriptor is configured, either the NWEO_TYPESPEC flag or the NWEO_TYPEANY flag is set for the ef_flags field of the ethernet file descriptor eth_fd, the only parameter of both hash_fd() and unhash_fd(). If NWEO_TYPEANY is set, any type of packet is accepted by the file descriptor. If NWEO_TYPESPEC if set, the nweo_type field of eth_fd is set to one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
After this configuration is done, hash_fd(eth_fd) is called to place the file descriptor in either the etp_type_any linked list (if the NWEO_TYPEANY flag is set) or in one of the linked lists in etp_type[] (there is a single linked list for each of the types shown above). Placing the file descriptors in these linked lists decreases the time needed to later find a file descriptor.
The figure below shows an example of an etp_type[] linked list with three ethernet file descriptors.

unhash_fd(eth_fd) removes the ethernet file descriptor from this linked list.
0025280
0025281 bf_afree(data);
0025282 reply_thr_get (eth_fd, result, TRUE);
0025283 return NW_OK;
0025284 }
0025285
0025286 case NWIOGETHOPT:
This case is never used by any of the upper layers (e.g., ip). However, if an ethernet device file (e.g., /dev/eth) is opened directly, eth_ioctl() can be called with a second argument of NWIOGETHOPT (by sr_rwio()). If this is the case, eth_ioctl() allocates an accessor with the size of a struct nwio_ethopt, fills the nwio_eth_opt struct with the settings of the ethernet options for the ethernet file descriptor, and returns the accessor.
0025287 {
0025288 nwio_ethopt_t *ethopt;
0025289 acc_t *acc;
0025290 int result;
0025291
0025292 acc= bf_memreq(sizeof(nwio_ethopt_t));
bf_memreq()
After the buffers have been initialized, accessors[] looks like the following:

bf_memreq() allocates accessors to the caller. For example, if 1514 bytes of buffer space are requested immediately after the network process starts and each buffer is 512 bytes (the default), then accessors[] will look like the following:

Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.
So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.
For a complete description of the network service's buffer management, click here.
0025293
0025294 ethopt= (nwio_ethopt_t *)ptr2acc_data(acc);
ptr2acc_data()
The macro ptr2acc_data is #define'd in inet/generic/buf.h as:
#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))
ptr2acc_data() simply returns a pointer to the actual data within an accessor.
ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.
0025295
0025296 *ethopt= eth_fd->ef_ethopt;
0025297
0025298 result= (*eth_fd->ef_put_userdata)(eth_fd->
0025299 ef_srfd, 0, acc, TRUE);
Copy the configuration data to the process that made the request.
The NWIOGETHOPT ioctl request can only be made if an ethernet device file (e.g., /dev/eth) is opened directly. Therefore, if an NWIOGETHOPT request is made, ef_put_userdata will be sr_put_userdata().
sr_put_userdata()
sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:
1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.
2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.
In my opinion, like sr_get_userdata(), this should have been made into two functions. As it is, it is too confusing.
0025300 if (result >= 0)
0025301 reply_thr_put(eth_fd, NW_OK, TRUE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025302 return result;
0025303 }
0025304 case NWIOGETHSTAT:
Only the arp code calls eth_ioctl() with the second parameter set to NWIOGETHSTAT. In this case, the ap_ethaddr field of the arp port is set to the ethernet address of the ethernet file descriptor's underlying ethernet port.
0025305 {
0025306 nwio_ethstat_t *ethstat;
0025307 acc_t *acc;
0025308 int result;
0025309
0025310 assert (sizeof(nwio_ethstat_t) <= BUF_S);
0025311
0025312 eth_port= eth_fd->ef_port;
0025313 if (!(eth_port->etp_flags & EPF_ENABLED))
0025314 {
0025315 reply_thr_put(eth_fd, EGENERIC, TRUE);
0025316 return NW_OK;
0025317 }
0025318
0025319 acc= bf_memreq(sizeof(nwio_ethstat_t));
bf_memreq()
After the buffers have been initialized, accessors[] looks like the following:

bf_memreq() allocates accessors to the caller. For example, if 1514 bytes of buffer space are requested immediately after the network process starts and each buffer is 512 bytes (the default), then accessors[] will look like the following:

Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.
So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.
For a complete description of the network service's buffer management, click here.
0025320 compare (bf_bufsize(acc), ==, sizeof(*ethstat));
0025321
0025322 ethstat= (nwio_ethstat_t *)ptr2acc_data(acc);
ptr2acc_data()
The macro ptr2acc_data is #define'd in inet/generic/buf.h as:
#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))
ptr2acc_data() simply returns a pointer to the actual data within an accessor.
ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.
0025323
0025324 ethstat->nwes_addr= eth_port->etp_ethaddr;
0025325
0025326 result= eth_get_stat(eth_port, ðstat->nwes_stat);
The ethernet task will fill in the nwio_ethstat struct with the ethernet port's statistical data.
eth_get_stat()
eth_get_stat(eth_port, eth_stat) sends a message to the ethernet task requesting the statistics for the ethernet port eth_port, eth_get_stat()'s first parameter. If successful, the ethernet task fills in the fields of the struct eth_stat_t eth_stat, eth_get_stat()'s second parameter.
eth_get_stat() is called only from eth_ioctl() in response to an NWIOGETHSTAT request.
0025327 assert (result == 0);
0025328 compare (bf_bufsize(acc), ==, sizeof(*ethstat));
0025329 result= (*eth_fd->ef_put_userdata)(eth_fd->
0025330 ef_srfd, 0, acc, TRUE);
If the arp code opened this ethernet file descriptor, ef_put_userdata is set to arp_putdata(). arp_putdata(), in this case, sets the ap_ethaddr field of the arp port to the ethernet address of the ethernet file descriptor's underlying ethernet port.
arp_putdata()
During the initialization of the network service, arp_main() calls eth_ioctl(). eth_ioctl(), in turn, (indirectly) calls arp_putdata() to get the underlying ethernet port's ethernet address.
After the initialization of the network service is complete, arp_putdata() is (indirectly) called by the ethernet code's packet2user() to deliver an arp-request or arp-reply packet to its destination arp port. arp_putdata() is also called by the ethernet code's reply_thr_put(), in which case arp_putdata() will call setup_read() to deliver any arp packets waiting to be delivered to the arp port.
0025331 if (result >= 0)
0025332 reply_thr_put(eth_fd, NW_OK, TRUE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025333 return result;
0025334 }
0025335 default:
0025336 break;
0025337 }
0025338 reply_thr_put(eth_fd, EBADIOCTL, TRUE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025339 return NW_OK;
0025340 }
0025341
0025342 PUBLIC int eth_write(fd, count)
0025343 int fd;
0025344 size_t count;
eth_write()
If a few tests (e.g., a test to determine if the ethernet packet is either too large or too small) have positive results and the ethernet task is not attempting to send an ethernet packet (i.e., etp_wr_pack is null) and the packet is coming from the ip code, eth_write(fd, count) passes the ethernet packet stored in the dl_eth.de_frame field of the ip port associated with the ethernet file descriptor fd, eth_write()'s first parameter, to eth_send().
If the packet is coming from the arp code (i.e., an arp-request or an arp-reply is being sent out), eth_write() calls arp_getdata() to create the ethernet packet before passing the newly created packet off to eth_send().
If the ethernet task is attempting to send an ethernet packet, eth_write() sets the ethernet port's EPF_MORE2WRITE flag and returns NW_SUSPEND.udp write path
For a write to a udp device (e.g., /dev/udp), the code takes the following path:
sr_rwio()
udp_write()
restart_write_fd()
ip_write()
ip_send()
if (packet is destined to a system on the local ethernet network) {
ipeth_send()
if (no previous packet being processed by ethernet task)
eth_send()
if (eth_send() can't immediately send packet)
eth_write()
}
else if (packet must be routed)
oroute_frag()
else if (packet ist destined for a local destination)
ev_enqueue()
0025345 {
0025346 eth_fd_t *eth_fd;
0025347 eth_port_t *eth_port;
0025348 acc_t *user_data;
0025349 int r;
0025350
Find the ethernet file descriptor whose index within eth_fd_table[] is fd, eth_write()'s first parameter. Then find the ethernet file descriptor's associated ethernet port.
0025351 eth_fd= ð_fd_table[fd];
0025352 eth_port= eth_fd->ef_port;
0025353
0025354 if (!(eth_fd->ef_flags & EFF_OPTSET))
If the ethernet file descriptor has not been configured (i.e., EFF_OPTSET has not been set), the file descriptor cannot be used. Send a message to the process that requested the write operation.
Note that the other protocols (e.g., udp, ip) also cannot use a file descriptor until the file descriptor has been configured.
0025355 {
0025356 reply_thr_get (eth_fd, EBADMODE, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025357 return NW_OK;
0025358 }
0025359
0025360 assert (!(eth_fd->ef_flags & EFF_WRITE_IP));
0025361
0025362 eth_fd->ef_write_count= count;
count is eth_write()'s second parameter. ef_write_count is used on lines 25379-25380, where the data is retrieved from the higher-layer code (e.g., ip).
0025363 if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY)
0025364 count += ETH_HDR_SIZE;
From ip(4):
"If the Ethernet header is completely specified by the nweo_flags (i.e.,
all of NWEO_EN_LOC, NWEO_DI_BROAD, NWEO_DI_MULTI, NWEO_DI_PROMISC,
NWEO_REMSPEC and NWEO_TYPESPEC are specified), then NWEO_RWDATONLY can be
used to send and receive only the data part of an Ethernet packet."
If the NWEO_RWDATONLY flag is set, the ethernet header will be retrieved on lines 25379-25380, along with the rest of the data.
0025365
0025366 if (count<ETH_MIN_PACK_SIZE || count>ETH_MAX_PACK_SIZE)
0025367 {
0025368 DBLOCK(1, printf("illegal packetsize (%d)\n",count));
0025369 reply_thr_get (eth_fd, EPACKSIZE, FALSE);
0025370 return NW_OK;
0025371 }
0025372 eth_fd->ef_flags |= EFF_WRITE_IP;
0025373 if (eth_port->etp_wr_pack)
If the ethernet task is already attempting to send out a packet, the write operation must be suspended.
0025374 {
0025375 eth_port->etp_flags |= EPF_MORE2WRITE;
0025376 return NW_SUSPEND;
0025377 }
0025378
0025379 user_data= (*eth_fd->ef_get_userdata)(eth_fd->ef_srfd, 0,
0025380 eth_fd->ef_write_count, FALSE);
ef_get_userdata is set to get_eth_data() for the ip code (port state will be IES_MAIN) and arp_getdata() for the arp code (port state will be ARS_ARPMAIN).
get_eth_data()
get_eth_data(fd, offset, count, for_ioctl) is (indirectly) called by a number of functions within the ethernet code, including eth_write(). get_eth_data() performs one of several tasks, depending on the state of the ip port and the value of count, get_eth_data()'s third parameter.
If the state of the ip port is IES_MAIN (its state during normal operations) and count is nonzero, get_eth_data() returns the packet from the de_frame field of the ip port. In this way, eth_write() gets the packet from the ip code to send off to eth_send().
If count is zero, get_eth_data() does something different. After eth_write() calls eth_send() (and the ethernet frame is therefore delivered), eth_write() calls the ethernet's reply_thr_get() with count equal to zero. If the ethernet file descriptor was opened up by the ip code, reply_thr_get() is simply a wrapper for eth_get_data(). In this scenario, get_eth_data() sets the ip port's de_frame field to null (since eth_send() just passed this packet to the ethernet driver) and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ip port's state is IES_PROTO (its configuration state), get_user_data() handles an initialization-related task. If count, get_eth_data()'s third parameter, is not zero (0), get_eth_data() sets various fields of an nwio_ethopt struct appropriate for the ip protocol and then returns a pointer to the struct.
When ipeth_main() calls eth_ioctl() the first time, eth_ioctl() in turn (indirectly) calls eth_get_data() to get the nwio_ethopt struct constructed by eth_get_data().
If count is zero and the ip port's state is IES_PROTO, get_eth_data() calls ipeth_main() if additional initialization is necessary.
arp_getdata()
arp_getdata(fd, offset, count, for_ioctl) accomplishes several different tasks, depending on the state of the arp port:
APS_ARPPROTO (configuration state):
If count, arp_getdata()'s third parameter, is non-zero, arp_getdata() creates a nwio_eth_opt struct and sets the appropriate flags and type for an ethernet file descriptor that services an arp port:
nweo_flags= NWEO_COPY|NWEO_EN_BROAD|NWEO_TYPESPEC;
nweo_type= HTONS(ETH_ARP_PROTO);
and then returns an accessor that contains the struct.
ARS_ARPMAIN (operational state):
If count, arp_getdata()'s third parameter, is non-zero, arp_getdata() creates an arp packet (an arp46_t struct) and returns the struct. eth_write() calls arp_getdata() to create an arp-request or arp_reply packet (the packets that build the arp table. The ip and ethernet address of the arp-reply or request is gotten from the ap_write_ipaddr and ap_write_ethaddr fields, respectively, of the arp port.
0025381 if (!user_data)
0025382 {
0025383 eth_fd->ef_flags &= ~EFF_WRITE_IP;
0025384 reply_thr_get (eth_fd, EFAULT, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025385 return NW_OK;
0025386 }
0025387 r= eth_send(fd, user_data, eth_fd->ef_write_count);
eth_send()
eth_send() does a couple of checks and sets some of the fields of the ethernet header before passing the packet off to ev_enqueue() (if the packet is destined for the local loopback) or eth_write_port() (if it is not).
0025388 assert(r == NW_OK);
0025389
0025390 eth_fd->ef_flags &= ~EFF_WRITE_IP;
0025391 reply_thr_get(eth_fd, eth_fd->ef_write_count, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025392 return NW_OK;
0025393 }
0025394
0025395 PUBLIC int eth_send(fd, data, data_len)
0025396 int fd;
0025397 acc_t *data;
0025398 size_t data_len;
eth_send()
eth_send() does a couple of checks and sets some of the fields of the ethernet header before passing the packet off to ev_enqueue() (if the packet is destined for the local loopback) or eth_write_port() (if it is not).udp write path
For a write to a udp device (e.g., /dev/udp), the code takes the following path:
sr_rwio()
udp_write()
restart_write_fd()
ip_write()
ip_send()
if (packet is destined to a system on the local ethernet network) {
ipeth_send()
if (no previous packet being processed by ethernet task)
eth_send()
if (eth_send() can't immediately send packet)
eth_write()
}
else if (packet must be routed)
oroute_frag()
else if (packet ist destined for a local destination)
ev_enqueue()
0025399 {
0025400 eth_fd_t *eth_fd;
0025401 eth_port_t *eth_port;
0025402 eth_hdr_t *eth_hdr;
0025403 acc_t *eth_pack;
0025404 unsigned long nweo_flags;
0025405 size_t count;
0025406 ev_arg_t ev_arg;
0025407
0025408 eth_fd= ð_fd_table[fd];
0025409 eth_port= eth_fd->ef_port;
Locate the ethernet file descriptor whose index within eth_fd_table[] is fd, the first parameter to eth_send() and find the file descriptor's associated ethernet port.
0025410
0025411 if (!(eth_fd->ef_flags & EFF_OPTSET))
0025412 return EBADMODE;
If the ethernet file descriptor has not been configured (i.e., the EFF_OPTSET flag in ef_flags has not been set), it cannot be used.
0025413
0025414 count= data_len;
0025415 if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY)
0025416 count += ETH_HDR_SIZE;
If the NWEO_RWDATONLY flag is set, data (the second parameter to eth_send()) consists of only data (without an ethernet header). In this case, the ethernet header is allocated on line 25430.
0025417
0025418 if (count<ETH_MIN_PACK_SIZE || count>ETH_MAX_PACK_SIZE)
0025419 {
0025420 DBLOCK(1, printf("illegal packetsize (%d)\n",count));
0025421 return EPACKSIZE;
0025422 }
0025423 if (eth_port->etp_wr_pack)
0025424 return NW_WOULDBLOCK;
Determine if there is another ethernet packet currently being sent by the ethernet driver. If so, that packet must be sent out first.
0025425
0025426 nweo_flags= eth_fd->ef_ethopt.nweo_flags;
0025427
0025428 if (nweo_flags & NWEO_RWDATONLY)
From ip(4):
"If the Ethernet header is completely specified by the nweo_flags (i.e.,
all of NWEO_EN_LOC, NWEO_DI_BROAD, NWEO_DI_MULTI, NWEO_DI_PROMISC,
NWEO_REMSPEC and NWEO_TYPESPEC are specified), then NWEO_RWDATONLY can be used to send and receive only the data part of an Ethernet packet."
In this flag is set, allocate memory for the ethernet header, which will precede the data.
0025429 {
0025430 eth_pack= bf_memreq(ETH_HDR_SIZE);
bf_memreq()
After the buffers have been initialized, accessors[] looks like the following:

bf_memreq() allocates accessors to the caller. For example, if 1514 bytes of buffer space are requested immediately after the network process starts and each buffer is 512 bytes (the default), then accessors[] will look like the following:

Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.
So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.
For a complete description of the network service's buffer management, click here.
0025431 eth_pack->acc_next= data;
0025432 }
0025433 else
0025434 eth_pack= bf_packIffLess(data, ETH_HDR_SIZE);
bf_packIffLess()
If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().
bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.
For a detailed description of the network service's buffer management, click here.
0025435
0025436 eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack);
ptr2acc_data()
The macro ptr2acc_data is #define'd in inet/generic/buf.h as:
#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))
ptr2acc_data() simply returns a pointer to the actual data within an accessor.
ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.
0025437
0025438 if (nweo_flags & NWEO_REMSPEC)
0025439 eth_hdr->eh_dst= eth_fd->ef_ethopt.nweo_rem;
From ip(4):
"NWEO_REMSPEC restricts sending and receiving of packets to the single remote computer specified in the nweo_rem field."
0025440
0025441 if (!(nweo_flags & NWEO_EN_PROMISC))
0025442 eth_hdr->eh_src= eth_port->etp_ethaddr;
If an ethernet file descriptor is in promiscuous mode, the file descriptor not only accepts any packet regardless of destination ethernet address but can also send out packets with any source ethernet address (not just the ethernet card's address).
If this is not the case, use the ethernet port ethernet address.
0025443
0025444 if (nweo_flags & NWEO_TYPESPEC)
0025445 eth_hdr->eh_proto= eth_fd->ef_ethopt.nweo_type;
From ip(4):
"NWEO_TYPESPEC restricts sending and receiving of packets to the type specified in nweo_type."
If the NWEO_TYPESPEC flag is set, the nweo_type field (see below) may be one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
0025446
0025447 if (eth_addrcmp(eth_hdr->eh_dst, eth_port->etp_ethaddr) == 0)
If the destination ethernet address is the same as the ethernet port's address, place an event in the system-wide event queue. The packet will then eventually be received and processed.
0025448 {
0025449 /* Local loopback. */
0025450 eth_port->etp_wr_pack= eth_pack;
0025451 ev_arg.ev_ptr= eth_port;
0025452 ev_enqueue(ð_port->etp_sendev, eth_loop_ev, ev_arg);
event_t / ev_enqueue() / ev_process() / ev_init() / ev_in_queue()
The event_t typedef is declared in inet/generic/event.h:
typedef struct event
{
ev_func_t ev_func;
ev_arg_t ev_arg;
struct event *ev_next;
} event_t;
If an event needs to be scheduled, ev_enqueue() is called to place the event in the system-wide event queue whose head is ev_head. ev_process() is eventually called from the main loop in inet.c to process the events. ev_in_queue(ev) simply returns TRUE if the event ev, ev_in_queue()'s only parameter, has a non-null value for func (see below) and FALSE if func is null. In this way, ev_in_queue() determines whether the event has been configured.
ev_init(ev) simply zeroes out the ev_func and ev_next fields of the event ev, ev_init()'s only parameter.
ev_func: A function (e.g., ip_process_loopb()) that performs some task.
ev_arg:
typedef union ev_arg
{
int ev_int;
void *ev_ptr;
} ev_arg_t;
ev_arg is ev_func's argument. In the case of a packet destined for the loopback address (127.0.0.1), the argument will be the ip port associated with the ip file descriptor that is sending out the packet. In the case of a message from the ethernet task that caused a deadlock, ev_arg is a pointer to the message's destination ethernet port.
ev_next: The next event in the system-wide event queue.
0025453 }
0025454 else
0025455 eth_write_port(eth_port, eth_pack);
eth_write_port()
eth_write_port(eth_port, pack) sends a message to the ethernet task (driver) requesting that the packet pack, eth_write_port()'s second parameter, be sent.
eth_write_port() is called only by eth_send(). By the time that eth_write_port() has been called, all layers (e.g., ethernet, ip, udp) have verified that the outgoing packet is valid and that the packet is destined for a remote system (i.e., is not destined for the local loopback or the address of the local port).
0025456 return NW_OK;
0025457 }
0025458
0025459 PUBLIC int eth_read (fd, count)
0025460 int fd;
0025461 size_t count;
eth_read()
eth_read() attempts to deliver all of the ethernet packets in an ethernet file descriptor's read queue to its associated ip port or arp port or sr file descriptor and returns NW_SUSPEND when there are no more ethernet packets to deliver. udp read path
eth_arrive()
ip_eth_arrived()
if (unicast packet)
ip_arrived()
else if (ethernet broadcast packet)
ip_arrived_broadcast()
if (packet must be input routed)
hand off packet to destination ip port
else
ip_port_arrive() {
packet2user()
udp_ip_arrived()
}
0025462 {
0025463 eth_fd_t *eth_fd;
0025464 acc_t *pack;
0025465
0025466 eth_fd= ð_fd_table[fd];
Find the ethernet file descriptor whose index within eth_fd_table[] is fd, eth_read()'s first parameter.
0025467 if (!(eth_fd->ef_flags & EFF_OPTSET))
0025468 {
0025469 reply_thr_put(eth_fd, EBADMODE, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025470 return NW_OK;
0025471 }
0025472 if (count < ETH_MAX_PACK_SIZE)
Unlike ip packets, an ethernet frame must have a specific length.
ETH_MAX_PACK_SIZE is #define'd in /include/net/gen/ether.h:
#define ETH_MAX_PACK_SIZE 1514
0025473 {
0025474 reply_thr_put(eth_fd, EPACKSIZE, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025475 return NW_OK;
0025476 }
0025477
0025478 assert(!(eth_fd->ef_flags & EFF_READ_IP));
0025479 eth_fd->ef_flags |= EFF_READ_IP;
0025480
0025481 while (eth_fd->ef_rdbuf_head)
ef_rdbuf_head points to the beginning of the ethernet file descriptor's queue of packets waiting to be read. The packets are linked by the acc_ext_link field of their accessors.
This while loop attempts to deliver each of the packets in an ethernet file descriptor's read queue.
0025482 {
0025483 pack= eth_fd->ef_rdbuf_head;
0025484 eth_fd->ef_rdbuf_head= pack->acc_ext_link;
0025485 if (get_time() <= eth_fd->ef_exp_time)
If the ethernet file descriptor's read queue is empty and packet2user() was not able to pass a packet that arrived from the ethernet task to the higher layer (e.g., the ip code), packet2user() places the packet in the read queue and sets the timer. Note that there is only a single timer for the read queue so if the timer has expired for the first packet, all of the packets will be discarded (i.e., bf_afree() on line 25492 will free all the packets without passing them up to the ip code).
get_time()
get_time() returns the number of clock ticks since reboot.
Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.
0025486 {
0025487 packet2user(eth_fd, pack, eth_fd->ef_exp_time);
packet2user() / eth
packet2user() attempts to move a packet to a higher layer (e.g., ip layer).
If the ethernet file descriptor is not currently being read (i.e., its EFF_READ_IP flag is not set), the packet is placed in the ethernet file descriptor's read queue instead of being passed to the higher layer. If the ethernet file descriptor's NWEO_RWDATONLY flag is set, the packet's header is removed before being moved to the higher layer.
0025488 if (!(eth_fd->ef_flags & EFF_READ_IP))
0025489 return NW_OK;
0025490 }
0025491 else
0025492 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025493 }
0025494 return NW_SUSPEND;
0025495 }
0025496
0025497 PUBLIC int eth_cancel(fd, which_operation)
0025498 int fd;
0025499 int which_operation;
eth_cancel()
If an operation has already been completed and the reply message to the file system is waiting in repl_queue, sr_rec() takes care of the cancellation. However, if the operation has not been completed, eth_cancel() cancels the operation by clearing the appropriate flag (e.g., EFF_READ_IP) and then sending the file system a message indicating that the operation has been interrupted.
eth_cancel() is called only to cancel an operation if an ethernet device file (e.g., /dev/eth) has been directly opened. eth_cancel() will not be called if a higher-layer device file (e.g., /dev/udp) has been opened.
0025500 {
0025501 eth_fd_t *eth_fd;
0025502
0025503 DBLOCK(2, printf("eth_cancel (%d)\n", fd));
0025504 eth_fd= ð_fd_table[fd];
Find the ethernet file descriptor whose index within eth_fd_table[] is fd, eth_write()'s first parameter.
0025505
0025506 switch (which_operation)
0025507 {
0025508 case SR_CANCEL_READ:
0025509 assert (eth_fd->ef_flags & EFF_READ_IP);
0025510 eth_fd->ef_flags &= ~EFF_READ_IP;
0025511 reply_thr_put(eth_fd, EINTR, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025512 break;
0025513 case SR_CANCEL_WRITE:
0025514 assert (eth_fd->ef_flags & EFF_WRITE_IP);
0025515 eth_fd->ef_flags &= ~EFF_WRITE_IP;
0025516 reply_thr_get(eth_fd, EINTR, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025517 break;
0025518 #if !CRAMPED
0025519 default:
0025520 ip_panic(( "got unknown cancel request" ));
0025521 #endif
0025522 }
0025523 return NW_OK;
0025524 }
0025525
0025526 PUBLIC void eth_close(fd)
0025527 int fd;
eth_close()
eth_close() simply closes a previously opened ethernet file descriptor. To be more specific, eth_close() removes the ethernet file descriptor from the appropriate linked list, frees all the packets in its read queue, and marks the ethernet file descriptor as available. In addition to this, eth_close() sends a message requesting to the ethernet task requesting that it change the receiving capabilities for the ethernet file descriptor's ethernet port.
eth_close() is called only to close an ethernet device file (e.g., /dev/eth) that has been directly opened. eth_close() will not be called if a higher-layer device file (e.g., /dev/udp) has been opened.
0025528 {
0025529 eth_fd_t *eth_fd;
0025530 eth_port_t *eth_port;
0025531 u32_t flags;
0025532 acc_t *pack;
0025533
0025534 eth_fd= ð_fd_table[fd];
Find the ethernet file descriptor whose index within eth_fd_table[] is fd, eth_write()'s first parameter.
0025535
0025536 assert ((eth_fd->ef_flags & EFF_INUSE) &&
0025537 !(eth_fd->ef_flags & EFF_BUSY));
0025538
0025539 if (eth_fd->ef_flags & EFF_OPTSET)
0025540 unhash_fd(eth_fd);
If the ethernet file descriptor was configured (i.e., its EFF_OPTSET flag was set), it was placed in the appropriate linked list.
hash_fd() / unhash_fd()
When an ethernet file descriptor is configured, either the NWEO_TYPESPEC flag or the NWEO_TYPEANY flag is set for the ef_flags field of the ethernet file descriptor eth_fd, the only parameter of both hash_fd() and unhash_fd(). If NWEO_TYPEANY is set, any type of packet is accepted by the file descriptor. If NWEO_TYPESPEC if set, the nweo_type field of eth_fd is set to one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
After this configuration is done, hash_fd(eth_fd) is called to place the file descriptor in either the etp_type_any linked list (if the NWEO_TYPEANY flag is set) or in one of the linked lists in etp_type[] (there is a single linked list for each of the types shown above). Placing the file descriptors in these linked lists decreases the time needed to later find a file descriptor.
The figure below shows an example of an etp_type[] linked list with three ethernet file descriptors.

unhash_fd(eth_fd) removes the ethernet file descriptor from this linked list.
0025541 while (eth_fd->ef_rdbuf_head != NULL)
0025542 {
0025543 pack= eth_fd->ef_rdbuf_head;
0025544 eth_fd->ef_rdbuf_head= pack->acc_ext_link;
0025545 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025546 }
0025547 eth_fd->ef_flags= EFF_EMPTY;
0025548
0025549 eth_port= eth_fd->ef_port;
0025550 flags= compute_rec_conf(eth_port);
compute_rec_conf()
compute_rec_conf() OR's the nweo_flag field for all of the configured ethernet file descriptors for a given ethernet port and returns the results. So, for example, if at least one of the ethernet file descriptors have the NWEO_PROMISC_MASK flag set, the result returned will reflect this.
0025551 eth_set_rec_conf(eth_port, flags);
eth_set_rec_conf ()
eth_set_rec_conf() sends a message requesting the ethernet task to change the receiving capabilities for an ethernet port. For example, if an ethernet file descriptor is being configured to receive broadcasts, eth_set_rec_conf() will send a message to the ethernet task requesting that broadcast packets for the port be accepted.
Note that eth_set_rec_conf() is called by eth_ioctl() for a NWIOSETHOPT (NetWork IO Set ETHernet OPTions) request. Since only root can open and configure the device file /dev/eth, only root can configure an ethernet file descriptor and, therefore, only root is able to place the ethernet card in promiscuous mode.
0025552 }
0025553
0025554 PUBLIC void eth_loop_ev(ev, ev_arg)
0025555 event_t *ev;
0025556 ev_arg_t ev_arg;
eth_loop_ev()
When the destination ethernet address of an ethernet packet is the same as the ethernet address of the ethernet port out of which the packet is being sent, an event is placed in the system-wide queue. When the system-wide event queue is processed, this event triggers a call to eth_loop_ev(), which sends the ethernet packet to eth_arrive() and then sets etp_wr_pack to null.
0025557 {
0025558 acc_t *pack;
0025559 eth_port_t *eth_port;
0025560
0025561 eth_port= ev_arg.ev_ptr;
0025562 assert(ev == ð_port->etp_sendev);
0025563
0025564 pack= eth_port->etp_wr_pack;
The ethernet packet in etp_wr_pack is the next frame to be processed. This frame will be processed either by the ethernet task (if the destination is remote) or by this function (if the destination is local).
0025565 eth_arrive(eth_port, pack, bf_bufsize(pack));
eth_arrive()
eth_arrive() is called when either the ethernet task receives an ethernet packet, when an ethernet multicast/broadcast packet is sent out of an ethernet port, or when an ethernet packet is destined for a local ethernet port. For a given packet, eth_arrive() finds the ethernet file descriptors that are interested in the packet. eth_arrive() then hands the packet off to the ip layer by calling either packet2user() or ip_eth_arrived() for these ethernet file descriptors.
0025566 eth_port->etp_wr_pack= NULL;
0025567 eth_restart_write(eth_port);
eth_restart_write()
eth_restart_write(eth_port) is called after the ethernet task successfully sends out an ethernet packet (if the destination of the ethernet packet is remote) or after the ethernet packet is handed off to its destination ethernet port (if the destination of the ethernet packet is local). If a second ethernet packet arrived from the ip layer while the previous packet was being sent out by the ethernet task, the packet will be placed in the ip port's dl_eth.de_frame field. eth_restart_write()'s task is to pass the ethernet packet on to eth_write() so that eth_write() can send the frame out.
0025568 }
0025569
0025570 PRIVATE int eth_checkopt (eth_fd)
0025571 eth_fd_t *eth_fd;
eth_checkopt()
eth_checkopt() verifies that an enable or disable flag for each set of flags (e.g., NWEO_ACC_MASK) is set. If this is the case, the EFF_OPTSET flag for the ethernet file descriptor is set and therefore the file descriptor can be used (e.g., written to/read from).
In addition to the verification of the flags, discard any packets in the read queue of the ethernet file descriptor. Since the options of the ethernet file descriptor are being changed, it is likely that the packets are no longer appropriate for this file descriptor.
0025572 {
0025573 /* bug: we don't check access modes yet */
0025574
0025575 unsigned long flags;
0025576 unsigned int en_di_flags;
0025577 eth_port_t *eth_port;
0025578 acc_t *pack;
0025579
0025580 eth_port= eth_fd->ef_port;
Lines 25581-25596 verify that an enable or disable flag for each set of flags (e.g., NWEO_ACC_MASK) is set. If this is the case, the EFF_OPTSET flag for the ethernet file descriptor is set and therefore the ethernet file descriptor can be used (e.g., written to/read from).
0025581 flags= eth_fd->ef_ethopt.nweo_flags;
0025582 en_di_flags= (flags >>16) | (flags & 0xffff);
0025583
0025584 if ((en_di_flags & NWEO_ACC_MASK) &&
0025585 (en_di_flags & NWEO_LOC_MASK) &&
0025586 (en_di_flags & NWEO_BROAD_MASK) &&
0025587 (en_di_flags & NWEO_MULTI_MASK) &&
0025588 (en_di_flags & NWEO_PROMISC_MASK) &&
0025589 (en_di_flags & NWEO_REM_MASK) &&
0025590 (en_di_flags & NWEO_TYPE_MASK) &&
0025591 (en_di_flags & NWEO_RW_MASK))
0025592 {
0025593 eth_fd->ef_flags |= EFF_OPTSET;
0025594 }
0025595 else
0025596 eth_fd->ef_flags &= ~EFF_OPTSET;
0025597
0025598 while (eth_fd->ef_rdbuf_head != NULL)
0025599 {
0025600 pack= eth_fd->ef_rdbuf_head;
0025601 eth_fd->ef_rdbuf_head= pack->acc_ext_link;
0025602 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025603 }
0025604
0025605 return NW_OK;
0025606 }
0025607
0025608 PRIVATE void hash_fd(eth_fd)
0025609 eth_fd_t *eth_fd;
hash_fd() / unhash_fd()
When an ethernet file descriptor is configured, either the NWEO_TYPESPEC flag or the NWEO_TYPEANY flag is set for the ef_flags field of the ethernet file descriptor eth_fd, the only parameter of both hash_fd() and unhash_fd(). If NWEO_TYPEANY is set, any type of packet is accepted by the file descriptor. If NWEO_TYPESPEC if set, the nweo_type field of eth_fd is set to one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
After this configuration is done, hash_fd(eth_fd) is called to place the file descriptor in either the etp_type_any linked list (if the NWEO_TYPEANY flag is set) or in one of the linked lists in etp_type[] (there is a single linked list for each of the types shown above). Placing the file descriptors in these linked lists decreases the time needed to later find a file descriptor.
The figure below shows an example of an etp_type[] linked list with three ethernet file descriptors.

unhash_fd(eth_fd) removes the ethernet file descriptor from this linked list.
0025610 {
0025611 eth_port_t *eth_port;
0025612 int hash;
0025613
Find the ethernet file descriptor's associated ethernet port and place the file descriptor at the beginning of the ethernet port's appropriate linked list.
0025614 eth_port= eth_fd->ef_port;
0025615 if (eth_fd->ef_ethopt.nweo_flags & NWEO_TYPEANY)
0025616 {
0025617 eth_fd->ef_type_next= eth_port->etp_type_any;
0025618 eth_port->etp_type_any= eth_fd;
0025619 }
0025620 else
The ethernet file descriptor only accepts packets of a specific high-layer protocol. The following example shows the calculations if the file descriptor only accepts ip packets.
0025621 {
0025622 hash= eth_fd->ef_ethopt.nweo_type;
0025623 hash ^= (hash >> 8);
hash ^= (hash >> 8)
hash = hash ^ (hash >> 8) =
0x800 ^ (0x8) = 1000 0000 0000 ^ 0000 0000 1000 (in binary) =
1000 0000 1000 (in binary) = 0x808
0025624 hash &= (ETH_TYPE_HASH_NR-1);
hash &= (ETH_TYPE_HASH_NR-1)
hash = hash & (ETH_TYPE_HASH_NR-1) = 0x808 & (16-1) = 0x808 & (0xF) = 8
Therefore, the ethernet file descriptor is placed in etp_type[8].
0025625
0025626 eth_fd->ef_type_next= eth_port->etp_type[hash];
0025627 eth_port->etp_type[hash]= eth_fd;
0025628 }
0025629 }
0025630
0025631 PRIVATE void unhash_fd(eth_fd)
0025632 eth_fd_t *eth_fd;
hash_fd() / unhash_fd()
When an ethernet file descriptor is configured, either the NWEO_TYPESPEC flag or the NWEO_TYPEANY flag is set for the ef_flags field of the ethernet file descriptor eth_fd, the only parameter of both hash_fd() and unhash_fd(). If NWEO_TYPEANY is set, any type of packet is accepted by the file descriptor. If NWEO_TYPESPEC if set, the nweo_type field of eth_fd is set to one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
After this configuration is done, hash_fd(eth_fd) is called to place the file descriptor in either the etp_type_any linked list (if the NWEO_TYPEANY flag is set) or in one of the linked lists in etp_type[] (there is a single linked list for each of the types shown above). Placing the file descriptors in these linked lists decreases the time needed to later find a file descriptor.
The figure below shows an example of an etp_type[] linked list with three ethernet file descriptors.

unhash_fd(eth_fd) removes the ethernet file descriptor from this linked list.
0025633 {
0025634 eth_port_t *eth_port;
0025635 eth_fd_t *prev, *curr, **eth_fd_p;
0025636 int hash;
0025637
Find the ethernet file descriptor's associated ethernet port and then determine to which of the linked lists the ethernet file descriptor belongs.
0025638 eth_port= eth_fd->ef_port;
0025639 if (eth_fd->ef_ethopt.nweo_flags & NWEO_TYPEANY)
0025640 {
0025641 eth_fd_p= ð_port->etp_type_any;
0025642 }
0025643 else
0025644 {
0025645 hash= eth_fd->ef_ethopt.nweo_type;
0025646 hash ^= (hash >> 8);
0025647 hash &= (ETH_TYPE_HASH_NR-1);
0025648
0025649 eth_fd_p= ð_port->etp_type[hash];
0025650 }
At this point, the linked list to which the ethernet file descriptor belongs has been found. Search the linked list for the file descriptor.
0025651 for (prev= NULL, curr= *eth_fd_p; curr;
0025652 prev= curr, curr= curr->ef_type_next)
0025653 {
0025654 if (curr == eth_fd)
0025655 break;
0025656 }
0025657 assert(curr);
Remove the ethernet file descriptor from the linked list by linking the file descriptor's former predecessor (if there was one) with its former successor.
0025658 if (prev)
0025659 prev->ef_type_next= curr->ef_type_next;
0025660 else
0025661 *eth_fd_p= curr->ef_type_next;
0025662 }
0025663
0025664 PUBLIC void eth_restart_write(eth_port)
0025665 eth_port_t *eth_port;
eth_restart_write()
eth_restart_write(eth_port) is called after the ethernet task successfully sends out an ethernet packet (if the destination of the ethernet packet is remote) or after the ethernet packet is handed off to its destination ethernet port (if the destination of the ethernet packet is local). If a second ethernet packet arrived from the ip layer while the previous packet was being sent out by the ethernet task, the packet will be placed in the ip port's dl_eth.de_frame field. eth_restart_write()'s task is to pass the ethernet packet on to eth_write() so that eth_write() can send the frame out.
0025666 {
0025667 eth_fd_t *eth_fd;
0025668 int i, r;
0025669
0025670 if (eth_port->etp_wr_pack)
0025671 return;
eth_restart_write() is called after a write operation successfully ends. If the etp_wr_pack field is not nonzero, eth_write_port() has not yet sent the packet out the ethernet port.
0025672
0025673 if (!(eth_port->etp_flags & EPF_MORE2WRITE))
0025674 return;
If the EPF_MORE2WRITE flag is not set, there are no ethernet packets waiting to be sent out the ethernet port.
0025675 eth_port->etp_flags &= ~EPF_MORE2WRITE;
0025676
0025677 for (i=0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++)
Go through all of the ethernet file descriptors looking for the ethernet packet waiting to be sent out (i.e., looking for the ethernet packet in the ip port's dl_eth.de_frame field).
0025678 {
0025679 if ((eth_fd->ef_flags & (EFF_INUSE|EFF_WRITE_IP)) !=
0025680 (EFF_INUSE|EFF_WRITE_IP))
eth_write() sets the EFF_WRITE_IP flag until the ethernet packet has been handed off to eth_send() (which typically hands off the packet to the ethernet task). If the ethernet task is busy and eth_write() therefore can't hand the packet off to eth_send(), the flag remains set.
The EFF_INUSE flag is set when the ethernet file descriptor is opened and is cleared when the file descriptor is closed.
0025681 {
0025682 continue;
0025683 }
0025684 if (eth_fd->ef_port != eth_port)
0025685 continue;
We're only concerned with ethernet file descriptors associated with the ethernet port eth_port, eth_restart_write()'s only parameter.
0025686
0025687 if (eth_port->etp_wr_pack)
0025688 {
0025689 eth_port->etp_flags |= EPF_MORE2WRITE;
0025690 return;
0025691 }
If the etp_wr_pack field is not null, the ethernet task is currently attempting to send out an ethernet packet. Set the EPF_MORE2WRITE flag to indicate that there is another packet waiting to be sent out after the ethernet task has finished sending its current packet.
0025692
0025693 eth_fd->ef_flags &= ~EFF_WRITE_IP;
0025694 r= eth_write(eth_fd-eth_fd_table, eth_fd->ef_write_count);
eth_write()
If a few tests (e.g., a test to determine if the ethernet packet is either too large or too small) have positive results and the ethernet task is not attempting to send an ethernet packet (i.e., etp_wr_pack is null) and the packet is coming from the ip code, eth_write(fd, count) passes the ethernet packet stored in the dl_eth.de_frame field of the ip port associated with the ethernet file descriptor fd, eth_write()'s first parameter, to eth_send().
If the packet is coming from the arp code (i.e., an arp-request or an arp-reply is being sent out), eth_write() calls arp_getdata() to create the ethernet packet before passing the newly created packet off to eth_send().
If the ethernet task is attempting to send an ethernet packet, eth_write() sets the ethernet port's EPF_MORE2WRITE flag and returns NW_SUSPEND.
0025695 assert(r == NW_OK);
0025696 }
0025697 }
0025698
0025699 PUBLIC void eth_arrive (eth_port, pack, pack_size)
0025700 eth_port_t *eth_port;
0025701 acc_t *pack;
0025702 size_t pack_size;
udp read path
eth_arrive()
ip_eth_arrived()
if (unicast packet)
ip_arrived()
else if (ethernet broadcast packet)
ip_arrived_broadcast()
if (packet must be input routed)
hand off packet to destination ip port
else
ip_port_arrive() {
packet2user()
udp_ip_arrived()
}
eth_arrive()
eth_arrive() is called when either the ethernet task receives an ethernet packet, when an ethernet multicast/broadcast packet is sent out of an ethernet port, or when an ethernet packet is destined for a local ethernet port. For a given packet, eth_arrive() finds the ethernet file descriptors that are interested in the packet. eth_arrive() then hands the packet off to the ip layer by calling either packet2user() or ip_eth_arrived() for these ethernet file descriptors.
0025703 {
0025704
0025705 eth_hdr_t *eth_hdr;
eth_hdr_t
An ethernet header is fairly simple. The eth_hdr_t typedef is declared in server/ip/gen/eth_hdr.h:
typedef struct eth_hdr
{
ether_addr_t eh_dst;
ether_addr_t eh_src;
ether_type_t eh_proto;
} eth_hdr_t;
ether_addr_t eh_dst: The destination ethernet address.
ether_addr_t eh_src: The source ethernet address.
ether_type_t eh_proto: The protocol of the layer above. The three possibilities are:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
An ethernet frame also has a CRC (Cyclic Redundancy Check) at its end to enable the receiving system to determine if corruption occured during transit.
An ethernet MAC (physical) address is a 48 bit number. This number is broken down into two halves: 22 of the first 24-bits identify the vendor of the Ethernet board (called the "Organizationally Unique Identifier") and the second 24-bits form a serial number assigned by the vendor. This guarantees that no two Ethernet cards have the same MAC address. One of the remaining bits indicate if the packet is a multicast or broadcast packet and the other is used for vendor-specific applications (e.g., NetBEUI).
+--+--+--+--+--+--+
| destination MAC |
+--+--+--+--+--+--+
| source MAC |
+--+--+--+--+--+--+
|08 00|
+--+--+-----------+
| |
. IP .
. packet .
. .
| |
+--+--+--+--+-----+
| CRC |
+--+--+--+--+
0025706 ether_addr_t *dst_addr;
0025707 int pack_stat;
0025708 ether_type_t type;
0025709 eth_fd_t *eth_fd, *first_fd, *share_fd;
0025710 int hash, i;
0025711 time_t exp_time;
0025712
0025713 exp_time= get_time() + EXPIRE_TIME;
EXPIRE_TIME is defined on line 25024:
#define EXPIRE_TIME 60*HZ /* seconds */
HZ is defined in include/minix/const.h:
#define HZ 60 /* clock freq (software settable on IBM-PC) */
get_time()
get_time() returns the number of clock ticks since reboot.
Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.
0025714
0025715 pack= bf_packIffLess(pack, ETH_HDR_SIZE);
bf_packIffLess()
If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().
bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.
For a detailed description of the network service's buffer management, click here.
0025716
0025717 eth_hdr= (eth_hdr_t*)ptr2acc_data(pack);
The ethernet header is obviously at the beginning of the packet.
ptr2acc_data()
The macro ptr2acc_data is #define'd in inet/generic/buf.h as:
#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))
ptr2acc_data() simply returns a pointer to the actual data within an accessor.
ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.
0025718 dst_addr= ð_hdr->eh_dst;
0025719
0025720 DIFBLOCK(0x20, dst_addr->ea_addr[0] != 0xFF &&
0025721 (dst_addr->ea_addr[0] & 0x1),
0025722 printf("got multicast packet\n"));
0025723
Determine whether the packet is a broadcast or multicast packet. If the packet is neither, determine if the packet's destination address matches the address of the ethernet port. This information helps locate the associated ethernet file descriptors (see line 25755).
0025724 if (dst_addr->ea_addr[0] & 0x1)
Determine if the address is a multicast or broadcast address.
A multicast address has the low-order bit of the high-order byte turned on. In hexadecimal representation, this bit looks like 01:00:00:00:00:00. (The ethernet broadcast address ff:ff:ff:ff:ff:ff can be considered a special case of the Ethernet multicast address.)
0025725 {
0025726 /* multi cast or broadcast */
0025727 if (eth_addrcmp(*dst_addr, broadcast) == 0)
eth_addrcomp()
eth_addrcmp() is #defined in eth.h:
#define eth_addrcmp(a,b) (memcmp((_VOIDSTAR)&a, (_VOIDSTAR)&b, sizeof(a)))
and (obviously) returns TRUE if the two addresses are the same.
memcmp() is defined in src/lib/ansi/memcmp.c and has the prototype:
memcmp(const void *s1, const void *s2, size_t n)
memcp() compares the two n byte chunks of memory, one chunk that starts at address s1 and the other that starts at address s2.
0025728 pack_stat= NWEO_EN_BROAD;
0025729 else
0025730 pack_stat= NWEO_EN_MULTI;
0025731 }
0025732 else
The ethernet packet is a unicast packet. Determine if the destination ethernet address of the unicast packet matches the ethernet address of the ethernet port.
0025733 {
0025734 if (eth_addrcmp (*dst_addr, eth_port->etp_ethaddr) == 0)
eth_addrcomp()
eth_addrcmp() is #defined in eth.h:
#define eth_addrcmp(a,b) (memcmp((_VOIDSTAR)&a, (_VOIDSTAR)&b, sizeof(a)))
and (obviously) returns TRUE if the two addresses are the same.
memcmp() is defined in src/lib/ansi/memcmp.c and has the prototype:
memcmp(const void *s1, const void *s2, size_t n)
memcp() compares the two n byte chunks of memory, one chunk that starts at address s1 and the other that starts at address s2.
0025735 pack_stat= NWEO_EN_LOC;
0025736 else
The destination address of the packet is not the address of the ethernet port. An ethernet file descriptor will only accept the packet if the file descriptor is in promiscuous mode.
0025737 pack_stat= NWEO_EN_PROMISC;
0025738 }
0025739 type= eth_hdr->eh_proto;
The protocol of the packet will be one of the following:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
The type is used to determine which linked lists of ethernet file descriptors associated with the ethernet port to search.
0025740 hash= type;
0025741 hash ^= (hash >> 8);
0025742 hash &= (ETH_TYPE_HASH_NR-1);
ETH_TYPE_HASH_NR is defined in inet/generic/eth_int.h:
#define ETH_TYPE_HASH_NR 16
Therefore, each ethernet port has 16 protocol-specific ethernet file descriptor linked lists.
0025743
0025744 first_fd= NULL;
0025745 for (i= 0; i<2; i++)
0025746 {
0025747 share_fd= NULL;
0025748
0025749 eth_fd= (i == 0) ? eth_port->etp_type_any :
0025750 eth_port->etp_type[hash];
Search through two of the ethernet port's linked lists of ethernet file descriptors. First, search through the linked list of file descriptors that have been configured to accept and send packets of any protocol type (e.g., ip). Second, search through the linked list associated with the specific protocol of the packet.
0025751 for (; eth_fd; eth_fd= eth_fd->ef_type_next)
ef_type_next links the ethernet file descriptors together in the etp_type_any and etp_type[] linked lists. Search through all of the file descriptors to find which file descriptors should be passed the packet.
0025752 {
0025753 if (i && eth_fd->ef_ethopt.nweo_type != type)
0025754 continue;
If an ethernet file descriptor has been configured to only accept packets of a certain protocol, reject all packets with protocols other than the configured protocol.
Again, here are the possible protocols:
#define ETH_RARP_PROTO 0x8035
#define ETH_ARP_PROTO 0x806
#define ETH_IP_PROTO 0x800
0025755 if (!(eth_fd->ef_ethopt.nweo_flags & pack_stat))
0025756 continue;
If the ethernet file descriptor doesn't accept local/broadcast/multicast packets, don't accept the packet if it is local/broadcast/multicast packet.
Note that if promiscuous mode is configured for an ethernet file descriptor, the ethernet file descriptor will not accept local, multicast, or broadcast packets unless the file descriptor is specifically configured to accept them.
0025757 if (eth_fd->ef_ethopt.nweo_flags & NWEO_REMSPEC &&
0025758 eth_addrcmp(eth_hdr->eh_src,
0025759 eth_fd->ef_ethopt.nweo_rem) != 0)
If an ethernet file descriptor is configured to only accept packets from a specific remote host, reject all packets not from this host.
0025760 {
0025761 continue;
0025762 }
From ip(4):
"If NWEO_SHARED is selected, then multiple channels (which all must select NWEO_SHARED) can use the same Ethernet type and they can all send packets. However, incoming packets will be delivered to at most one of them."
The following lines determine which ethernet file descriptor receives the ethernet packet.
0025763 if ((eth_fd->ef_ethopt.nweo_flags & NWEO_ACC_MASK) ==
0025764 NWEO_SHARED)
0025765 {
0025766 if (!share_fd)
0025767 {
0025768 share_fd= eth_fd;
0025769 continue;
0025770 }
0025771 if (!eth_fd->ef_rdbuf_head)
0025772 share_fd= eth_fd;
In order to improve efficiency, a packet is delivered to at most one of the ethernet file descriptors that are in shared mode. The "best" candidate will be the ethernet file descriptor that has no data waiting.
0025773 continue;
0025774 }
0025775 if (!first_fd)
0025776 {
0025777 first_fd= eth_fd;
0025778 continue;
0025779 }
0025780 pack->acc_linkC++;
If the code gets to this point, more than one ethernet file descriptor is interested in this ethernet packet. Increment the acc_link field of the ethernet packet so that packet2user() will make a duplicate of the ethernet packet.
0025781 packet2user(eth_fd, pack, exp_time);
packet2user() / eth
packet2user() attempts to move a packet to a higher layer (e.g., ip layer).
If the ethernet file descriptor is not currently being read (i.e., its EFF_READ_IP flag is not set), the packet is placed in the ethernet file descriptor's read queue instead of being passed to the higher layer. If the ethernet file descriptor's NWEO_RWDATONLY flag is set, the packet's header is removed before being moved to the higher layer.
0025782 }
0025783 if (share_fd)
0025784 {
0025785 pack->acc_linkC++;
0025786 packet2user(share_fd, pack, exp_time);
packet2user() / eth
packet2user() attempts to move a packet to a higher layer (e.g., ip layer).
If the ethernet file descriptor is not currently being read (i.e., its EFF_READ_IP flag is not set), the packet is placed in the ethernet file descriptor's read queue instead of being passed to the higher layer. If the ethernet file descriptor's NWEO_RWDATONLY flag is set, the packet's header is removed before being moved to the higher layer.
0025787 }
0025788 }
0025789 if (first_fd)
0025790 {
0025791 if (first_fd->ef_put_pkt &&
0025792 (first_fd->ef_flags & EFF_READ_IP) &&
0025793 !(first_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY))
If the EFF_READ_IP flag is not set, packet2user() places the ethernet packet in the read queue. If the destination ethernet file descriptor's NWEO_RWDATONLY flag is set, packet2user() strips off the ethernet header. After these two checks, packet2user() calls the upper-layer functions that hand the packet to the next layer (e.g., ip_eth_arrived()). Of course, if the EFF_READ_IP flag is not set and the ethernet file descriptor's NWEO_RWDATONLY flag is not set, the upper-layer function should be called directly.
0025794 {
0025795 (*first_fd->ef_put_pkt)(first_fd->ef_srfd, pack,
0025796 pack_size);
If the ethernet file descriptor was opened by the ip code, ef_put_pkt will be set to a reference of ipeth_init().
ip_eth_arrived()
ip_eth_arrived() is called by the ethernet code (e.g., packet2user()) to hand off a packet to the ip code. ip_eth_arrived() strips off the ethernet header before handing the packet off to ip_arrived() (if the packet is not an ethernet broadcast packet) or ip_arrived_broadcast() (if it is).
0025797 }
0025798 else
0025799 packet2user(first_fd, pack, exp_time);
packet2user() / eth
packet2user() attempts to move a packet to a higher layer (e.g., ip layer).
If the ethernet file descriptor is not currently being read (i.e., its EFF_READ_IP flag is not set), the packet is placed in the ethernet file descriptor's read queue instead of being passed to the higher layer. If the ethernet file descriptor's NWEO_RWDATONLY flag is set, the packet's header is removed before being moved to the higher layer.
0025800 }
0025801 else
The packet wasn't destined for any ethernet file descriptor whose access flag was NWEO_EXCL or NWEO_COPY. Discard the packet.
0025802 {
0025803 if (pack_stat == NWEO_EN_LOC)
0025804 {
0025805 DBLOCK(0x01,
0025806 printf("eth_arrive: dropping packet for proto 0x%x\n",
0025807 ntohs(type)));
0025808 }
0025809 else
0025810 {
0025811 DBLOCK(0x20, printf("dropping packet for proto 0x%x\n",
0025812 ntohs(type)));
0025813 }
0025814 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025815 }
0025816 }
0025817
0025818 PRIVATE void packet2user (eth_fd, pack, exp_time)
0025819 eth_fd_t *eth_fd;
0025820 acc_t *pack;
0025821 time_t exp_time;
packet2user() / eth
packet2user() attempts to move a packet to a higher layer (e.g., ip layer).
If the ethernet file descriptor is not currently being read (i.e., its EFF_READ_IP flag is not set), the packet is placed in the ethernet file descriptor's read queue instead of being passed to the higher layer. If the ethernet file descriptor's NWEO_RWDATONLY flag is set, the packet's header is removed before being moved to the higher layer.
0025822 {
0025823 int result;
0025824 acc_t *tmp_pack;
0025825 size_t size;
0025826
0025827 assert (eth_fd->ef_flags & EFF_INUSE);
0025828 if (!(eth_fd->ef_flags & EFF_READ_IP))
If the ethernet file descriptor is not currently being read, make a copy of the packet (if the packet is being shared) and place the packet in the ethernet file descriptor's read buffer.
0025829 {
0025830 if (pack->acc_linkC != 1)
If there are multiple references to this packet, copy the packet and decrease the reference count of the original packet by one.
0025831 {
0025832 tmp_pack= bf_dupacc(pack);
bf_dupacc()
bf_dupacc(acc_ptr) creates a new accessor that is a duplicate of acc_ptr, bf_dupacc()'s only parameter.
More specifically, bf_dupacc() removes an accessor from acc_freelist and copies the accessor referred to by acc_ptr and sets acc_linkC of the new accessor to one. If acc_next is non-null, bf_dupacc() also increments acc_linkC of acc_next (the next accessor in the linked list). And if acc_buffer is non-null, bf_dupacc() also increments buf_linkC of the buffer.
This process is best described by a diagram:

Note that the link counts (acc_linkC and buf_linkC) for accessors[65] and buffers512[127] are incremented.
Remember that free accessors associated with buffers reside on buf512_freelist and free accessors not associated with buffers reside on acc_freelist. In addition, acc_linkC is one or greater if the accessor is no longer on either of the freelists and is greater than one if more than one accessor refers to it (through acc_next). buf_linkC is one or greater if its associated accessor (or accessors) are not on buf512_freelist and is greater than one if more than one accessor refers to it (through acc_buffer).
0025833 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025834 pack= tmp_pack;
0025835 tmp_pack= NULL;
0025836 }
Place the packet at the tail of the read buffer queue (which will also be the head if there are no pre-existing packets in the queue).
0025837 pack->acc_ext_link= NULL;
0025838 if (eth_fd->ef_rdbuf_head == NULL)
0025839 {
0025840 eth_fd->ef_rdbuf_head= pack;
0025841 eth_fd->ef_exp_time= exp_time;
This expiration time was set before any packets were processed. The expiration time is only set for the first packet in the buffer. If the first packet expires before being processed, this packet and all subsequent packets are discarded.
0025842 }
0025843 else
0025844 eth_fd->ef_rdbuf_tail->acc_ext_link= pack;
0025845 eth_fd->ef_rdbuf_tail= pack;
0025846 return;
0025847 }
0025848
0025849 if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY)
0025850 pack= bf_delhead(pack, ETH_HDR_SIZE);
Remove the ethernet header if the ethernet file descriptor is not interested in the header (i.e., the ethernet file descriptor's NWEO_RWDATONLY flag is set).
From ip(4):
"NWEO_RWDATONLY can be used to send and receive only the data part of an Ethernet packet."
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:

bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0025851
0025852 size= bf_bufsize(pack);
0025853
0025854 if (eth_fd->ef_put_pkt)
0025855 {
0025856 (*eth_fd->ef_put_pkt)(eth_fd->ef_srfd, pack, size);
If the ip code opened the ethernet file descriptor, ef_put_pkt is set to ip_eth_arrived() by eth_open(). The ef_srfd field of an ethernet file descriptor is the corresponding port of the higher layer (e.g., an ip port).
ip_eth_arrived()
ip_eth_arrived() is called by the ethernet code (e.g., packet2user()) to hand off a packet to the ip code. ip_eth_arrived() strips off the ethernet header before handing the packet off to ip_arrived() (if the packet is not an ethernet broadcast packet) or ip_arrived_broadcast() (if it is).
0025857 return;
0025858 }
0025859
0025860 eth_fd->ef_flags &= ~EFF_READ_IP;
0025861 result= (*eth_fd->ef_put_userdata)(eth_fd->ef_srfd, (size_t)0, pack,
0025862 FALSE);
If the arp code opened the ethernet file descriptor, ef_put_userdata is set to arp_putdata().
arp_putdata()
During the initialization of the network service, arp_main() calls eth_ioctl(). eth_ioctl(), in turn, (indirectly) calls arp_putdata() to get the underlying ethernet port's ethernet address.
After the initialization of the network service is complete, arp_putdata() is (indirectly) called by the ethernet code's packet2user() to deliver an arp-request or arp-reply packet to its destination arp port. arp_putdata() is also called by the ethernet code's reply_thr_put(), in which case arp_putdata() will call setup_read() to deliver any arp packets waiting to be delivered to the arp port.
0025863 if (result >=0)
0025864 reply_thr_put(eth_fd, size, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025865 else
0025866 reply_thr_put(eth_fd, result, FALSE);
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025867 }
0025868
0025869 PRIVATE void eth_buffree (priority)
0025870 int priority;
eth_buffree()
eth_buffree(priority) is called by bf_memreq() if bf_memreq() does not have enough accessors to satisfy a buffer request.
priority, eth_buffree()'s only parameter, will be either ETH_PRI_FDBUFS_EXTRA (#define'd as 5) or ETH_PRI_FDBUFS (6). If priority is ETH_PRI_FDBUFS_EXTRA, every packet except the last packet in the read queue of each ethernet file descriptor is freed. If priority is ETH_PRI_FDBUFS_EXTRA, all packets in the read queues of all ethernet file descriptors are freed.
0025871 {
0025872 int i;
0025873 eth_fd_t *eth_fd;
0025874 acc_t *pack;
0025875
0025876 if (priority == ETH_PRI_FDBUFS_EXTRA)
If priority is ETH_PRI_FDBUFS_EXTRA, every packet except the last packet in the read queue of each ethernet file descriptor is freed.
0025877 {
0025878 for (i= 0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++)
0025879 {
0025880 while (eth_fd->ef_rdbuf_head &&
0025881 eth_fd->ef_rdbuf_head->acc_ext_link)
0025882 {
0025883 pack= eth_fd->ef_rdbuf_head;
0025884 eth_fd->ef_rdbuf_head= pack->acc_ext_link;
0025885 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025886 }
0025887 }
0025888 }
0025889 if (priority == ETH_PRI_FDBUFS)
If priority is ETH_PRI_FDBUFS_EXTRA, all packets in the read queues of all ethernet file descriptors are freed.
0025890 {
0025891 for (i= 0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++)
0025892 {
0025893 while (eth_fd->ef_rdbuf_head)
0025894 {
0025895 pack= eth_fd->ef_rdbuf_head;
0025896 eth_fd->ef_rdbuf_head= pack->acc_ext_link;
0025897 bf_afree(pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:

Then the resulting chain will be:

bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0025898 }
0025899 }
0025900 }
0025901 }
0025902
0025903 #ifdef BUF_CONSISTENCY_CHECK
0025904 PRIVATE void eth_bufcheck()
0025905 {
0025906 int i;
0025907 eth_fd_t *eth_fd;
0025908 acc_t *pack;
0025909
0025910 for (i= 0; i<eth_conf_nr; i++)
0025911 {
0025912 bf_check_acc(eth_port_table[i].etp_rd_pack);
0025913 bf_check_acc(eth_port_table[i].etp_wr_pack);
0025914 }
0025915 for (i= 0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++)
0025916 {
0025917 for (pack= eth_fd->ef_rdbuf_head; pack;
0025918 pack= pack->acc_ext_link)
0025919 {
0025920 bf_check_acc(pack);
0025921 }
0025922 }
0025923 }
0025924 #endif
0025925
0025926 PRIVATE u32_t compute_rec_conf(eth_port)
0025927 eth_port_t *eth_port;
compute_rec_conf()
compute_rec_conf() OR's the nweo_flag field for all of the configured ethernet file descriptors for a given ethernet port and returns the results. So, for example, if at least one of the ethernet file descriptors have the NWEO_PROMISC_MASK flag set, the result returned will reflect this.
0025928 {
0025929 eth_fd_t *eth_fd;
0025930 u32_t flags;
0025931 int i;
0025932
0025933 flags= NWEO_NOFLAGS;
0025934 for (i=0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++)
0025935 {
0025936 if ((eth_fd->ef_flags & (EFF_INUSE|EFF_OPTSET)) !=
0025937 (EFF_INUSE|EFF_OPTSET))
0025938 {
0025939 continue;
0025940 }
Only OR the flags for the ethernet file descriptors for a given ethernet port that are currently in use and configured.
0025941 if (eth_fd->ef_port != eth_port)
0025942 continue;
0025943 flags |= eth_fd->ef_ethopt.nweo_flags;
0025944 }
0025945 return flags;
0025946 }
0025947
0025948 PRIVATE void reply_thr_get (eth_fd, result, for_ioctl)
0025949 eth_fd_t *eth_fd;
0025950 size_t result;
0025951 int for_ioctl;
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025952 {
0025953 acc_t *data;
0025954
0025955 data= (*eth_fd->ef_get_userdata)(eth_fd->ef_srfd, result, 0, for_ioctl);
get_eth_data()
get_eth_data(fd, offset, count, for_ioctl) is (indirectly) called by a number of functions within the ethernet code, including eth_write(). get_eth_data() performs one of several tasks, depending on the state of the ip port and the value of count, get_eth_data()'s third parameter.
If the state of the ip port is IES_MAIN (its state during normal operations) and count is nonzero, get_eth_data() returns the packet from the de_frame field of the ip port. In this way, eth_write() gets the packet from the ip code to send off to eth_send().
If count is zero, get_eth_data() does something different. After eth_write() calls eth_send() (and the ethernet frame is therefore delivered), eth_write() calls the ethernet's reply_thr_get() with count equal to zero. If the ethernet file descriptor was opened up by the ip code, reply_thr_get() is simply a wrapper for eth_get_data(). In this scenario, get_eth_data() sets the ip port's de_frame field to null (since eth_send() just passed this packet to the ethernet driver) and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ip port's state is IES_PROTO (its configuration state), get_user_data() handles an initialization-related task. If count, get_eth_data()'s third parameter, is not zero (0), get_eth_data() sets various fields of an nwio_ethopt struct appropriate for the ip protocol and then returns a pointer to the struct.
When ipeth_main() calls eth_ioctl() the first time, eth_ioctl() in turn (indirectly) calls eth_get_data() to get the nwio_ethopt struct constructed by eth_get_data().
If count is zero and the ip port's state is IES_PROTO, get_eth_data() calls ipeth_main() if additional initialization is necessary.
0025956 assert (!data);
0025957 }
0025958
0025959 PRIVATE void reply_thr_put (eth_fd, result, for_ioctl)
0025960 eth_fd_t *eth_fd;
0025961 size_t result;
0025962 int for_ioctl;
reply_thr_get() / reply_thr_put() / eth
reply_thr_get() and reply_thr_put() are wrappers for the ethernet port's ef_get_userdata field and ef_put_userdata.
If the ethernet file descriptor was opened by the ip code, ef_get_userdata is get_eth_data(). If this is the case and reply_thr_get() is called, get_eth_data() simply sets de_frame to null and calls ipeth_restart_send() if there are any ethernet packets that the ip code is waiting to send.
If the ethernet file descriptor was opened by the ip code, ef_put_userdata is put_eth_data(). If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025963 {
0025964 int error;
0025965
0025966 error= (*eth_fd->ef_put_userdata)(eth_fd->ef_srfd, result, (acc_t *)0,
0025967 for_ioctl);
put_eth_data()
put_eth_data(port, offset, data, for_ioctl) is called only by reply_thr_put() with data, put_eth_data()'s third parameter, set to null. If there are no ethernet packets waiting to be delivered to the ip port, put_eth_data() simply clears the IEF_READ_IP flag. If data is null and there are ethernet packets waiting to be delivered to the ip port, put_eth_data() calls do_eth_read() to process the packets.
0025968 assert(error == NW_OK);
0025969 }
0025970
0025971 /*
0025972 * $PchId: eth.c,v 1.11 1996/08/02 07:04:58 philip Exp $
0025973 */