Please wait until the page is fully downloaded and then press the "Expand" button or the blue line numbers.

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(&eth_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;
ETH_TYPE_HASH_NR is #define'd in eth_int.h as 16.


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= &eth_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;
Verify that the port has been initialized. EPF_ENABLED is set in osdep_eth_init().


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= &eth_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;
If opened by the ip code, srfd is the ip port associated with the ethernet file descriptor.


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;
Return the index of the new ethernet file descriptor.


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= &eth_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,
"en" stands for enable and "di" stands for disable.


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)
There is no point to both enabling and disabling any of the flags. Return an error.


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;
This check is pointless since eth_optcheck() always returns NW_OK.


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;
Copy the ethernet file descriptor's ethernet options to the newly allocated struct.


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, &ethstat->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= &eth_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)
Verify that the size of the packet will be within the allowable range.


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)
There's a problem if the packet couldn't be retrieved from the upper layer (e.g., ip).


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= &eth_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)
Verify that the packet is between the minimum and maximum acceptable packet length.


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
Otherwise, the ethernet header is already included in the data.


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(&eth_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
The destination is remote. Pass the packet off to eth_write_port().


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= &eth_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))
An ethernet file descriptor must be configured before it may be used.


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;
Set the EFF_READ_IP flag since a read operation is about to begin.


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= &eth_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= &eth_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)
Discard the ethernet packets in the ethernet file descriptor's read queue.


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;
Mark the ethernet file descriptor as available.


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;
The destination ethernet port of the ethernet packet is the ev_ptr field of ev_arg.


0025562          assert(ev == &eth_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 
Discard any packets in the read queue of the ethernet file descriptor.


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;
hash = 0x800 (#define ETH_IP_PROTO 0x800)


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= &eth_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= &eth_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= &eth_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);
^ is the bitwise exclusive OR operator. For example:

0101 ^ 0110 = 0011


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)
The first ethernet file descriptor encountered will be processed on line 25789.


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  */