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

0035001 /*
0035002 ip.c
0035003 
0035004 Copyright 1995 Philip Homburg
0035005 */
ip.c contains code that is not specific to read, write, or ioctl operations. ip.c contains the initialization functions and the functions that open and close ip file descriptors. In addition to this, ip.c contains the function that cancels previous requests as well as the function that frees accessors that the ip layer has acquired.


0035006 
0035007 #include "inet.h"
0035008 #include "buf.h"
0035009 #include "event.h"
0035010 #include "type.h"
0035011 
0035012 #include "arp.h"
0035013 #include "assert.h"
0035014 #include "clock.h"
0035015 #include "eth.h"
0035016 #include "icmp.h"
0035017 #include "icmp_lib.h"
0035018 #include "io.h"
0035019 #include "ip.h"
0035020 #include "ip_int.h"
0035021 #include "ipr.h"
0035022 #include "sr.h"
0035023 
0035024 THIS_FILE
0035025 
0035026 FORWARD void ip_close ARGS(( int fd ));
0035027 FORWARD int ip_cancel ARGS(( int fd, int which_operation ));
0035028 
0035029 FORWARD void ip_buffree ARGS(( int priority ));
0035030 #ifdef BUF_CONSISTENCY_CHECK
0035031 FORWARD void ip_bufcheck ARGS(( void ));
0035032 #endif
0035033 FORWARD void ip_bad_callback ARGS(( struct ip_port *ip_port ));
0035034 
0035035 PUBLIC ip_port_t *ip_port_table;
ip_port / ip_port_table[]

For every interface listed in inet.conf, there is a single ip port. For example, for the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

there will be an ip port associated with the ethernet interface and an ip port associated with the psip interface. Each of these ip ports is a struct ip_port (see below) and each ip_port struct is in ip_port_table[]. So, for the example inet.conf file above, ip_port_table[] will have 2 elements; ip_port_table[0] will be for the ethernet interface and ip_port_table[1] will be for the psip interface.

Each element in ip_port_table[] is associated with several ip file descriptors. For example, the udp code (during initialization) will open up an ip file descriptor and this ip file descriptor will be associated with one of the elements in ip_port_table[].



typedef struct ip_port

{
int ip_flags, ip_dl_type;
int ip_port;
union
{
struct
{
int de_state;
int de_flags;
int de_port;
int de_fd;
acc_t *de_frame;
acc_t *de_q_head;
acc_t *de_q_tail;
acc_t *de_arp_head;
acc_t *de_arp_tail;
} dl_eth;
struct
{
int ps_port;
acc_t *ps_send_head;
acc_t *ps_send_tail;
} dl_ps;
} ip_dl;
ipaddr_t ip_ipaddr;
ipaddr_t ip_netmask;
ipaddr_t ip_subnetmask;
u16_t ip_frame_id;
u16_t ip_mss;
ip_dev_t ip_dev_main;
ip_dev_t ip_dev_set_ipaddr;
ip_dev_send_t ip_dev_send;
acc_t *ip_loopb_head;
acc_t *ip_loopb_tail;
event_t ip_loopb_event;
struct ip_fd *ip_proto_any;
struct ip_fd *ip_proto[IP_PROTO_HASH_NR];
} ip_port_t;



int ip_flags:

The possible ip_flags are #define'd in ip_int.h:

#define IPF_EMPTY 0x0
#define IPF_CONFIGURED 0x1
#define IPF_IPADDRSET 0x2
#define IPF_NETMASKSET 0x4

After the initialization of the ip port, ip_flags is set to IPF_CONFIGURED. If the "ifconfig -h host-IP-address" command is issued, ip_ioctl() sets the IPF_IPADDRSET flag before setting the ip address and (optionally) the subnet mask.


int ip_dl_type:

"dl" stands for "data link" (layer). ip_dl_type is set to the corresponding data link layer type of the port. These types include NETTYPE_ETH (ethernet) and NETTYPE_PSIP (psip).


int ip_port:

The port number of the ip device. For example, for a system with the following /etc/inet.conf file:

eth0 DP8390 0 { default; };
psip1;

there will be 2 ports: port 0 for the ethernet device and port 1 for the psip device.

Note that this port will not necessarily be the same as dl_eth.de_port (see below).


struct dl_eth: The dl_eth struct is used (instead of dl_ps) if the underlying data link layer device of this port is an ethernet device.


int de_state:

The possible de_state values are #define'd in ip_int.h:

#define IES_EMPTY 0x0
#define IES_SETPROTO 0x1
#define IES_GETIPADDR 0x2
#define IES_MAIN 0x3
#define IES_ERROR 0x4

When the ip port is being initialized, de_state changes in quick succession from IES_EMPTY to IES_SETPROTO to IES_GETIPADDR before entering IES_MAIN, which is its normal operational state.


int de_flags:

de_flags is initialized to IEF_EMPTY. Note that "SP" stands for "SusPend".

#define IEF_EMPTY 0x1
#define IEF_SUSPEND 0x8
#define IEF_READ_IP 0x10
#define IEF_READ_SP 0x20
#define IEF_WRITE_SP 0x80


int de_port:

The ethernet port number. For example, if there were two ethernet devices, one ethernet device would have port 0 and the other would have port 1, regardless of how many psip devices were on the system.

This value is initialized in ip_init(). Also see the initial comments in ip_config.c for a description of ip_conf[].

Note that this port will not necessarily be the same as ip_port (see above).


int de_fd:

Initialized by calling eth_open(), de_fd is the ip port's associated ethernet file descriptor.


acc_t *de_frame:
acc_t *de_q_head:
acc_t *de_q_tail:


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 an ethernet port's 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 the ip port to send off to the ethernet port, the ip port encapsulates the ip packet with an ethernet header and the resulting ethernet packet is placed in the dl_eth.de_frame field of the ip port. If the ip port 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 *de_arp_head:
acc_t *de_arp_tail:


If the arp table (i.e., arp cache) does not contain an entry for a given ip address, arp_ip_eth() returns NW_SUSPEND and the outgoing packet is placed in the ip port's de_arp_head/de_arp_tail queue. Before being placed in this queue, the packet is encapsulated in a "xmit" header (as opposed to an ethernet header).

xmit_hdr is declared in generic/ip_eth.c:

typedef struct xmit_hdr

{
time_t xh_time;
ipaddr_t xh_ipaddr;
} xmit_hdr_t;
where xh_time is the time at which the packet is placed in the de_arp_head/de_arp_tail queue and xh_ipaddr is the destination ip address of the packet.


struct dl_ps:
int ps_port:
acc_t *ps_send_head:
acc_t *ps_send_tail:


The dl_ps struct is used (instead of dl_eth) if the underlying data link layer device is a psip device. Coverage of psip is not included in this documentation.


ipaddr_t ip_ipaddr:
ipaddr_t ip_netmask:
ipaddr_t ip_subnetmask:


The ip address of a port can be set in two ways, either with RARP or through the "ifconfig -h host-IP-address" command. If set by the ifconfig command, a message requesting an NWIOSIPCONF (Set IP CONFiguration) is sent to the appropriate ip device (e.g., /dev/ip00, causing ip_ioctl() to be called. The user then passes in a nwio_ipconf struct which contains either the ip address or the subnet mask or both.

The netmask is simply a reflection of the class to which the ip address belongs. For example, if the ip address is 194.77.33.5, then it is a class C address and its netmask is therefore 255.255.255.0. See ip_nettype() for more information.


u16_t ip_frame_id:

ip_frame_id is initialized to the time at which the ip port was configured and incremented each time a packet is sent out. The ih_id field of each packet's ip header is set to ip_frame_id. If a packet is fragmented, the receiver can properly order the framents.


u16_t ip_mss:

If the ip_port_table[] element has an underlying ethernet layer, ip_mss is initialized to ETH_MAX_PACK_SIZE-ETH_HDR_SIZE (1514-14=1500), the size of the payload of an ethernet packet in bytes. If the size of the resulting ip packet is too large, the code fragments the packet.


ip_dev_t ip_dev_main:

ip_dev_main is initialized to ipeth_main() for ethernet devices. This function is called in ip_init().


ip_dev_t ip_dev_set_ipaddr:

ip_dev_set_ipaddr is initialized to ipeth_set_ipaddr() for ethernet devices.


ip_dev_send_t ip_dev_send:

ip_dev_send is initialized to ipeth_send() for ethernet devices or ipps_send() for psip devices.


acc_t *ip_loopb_head:
acc_t *ip_loopb_tail:
event_t ip_loopb_event:


Ip packets destined for the loopback address (127.0.0.1) or destined for the ip address of the ip port itself are placed in the ip_loopb_head/ip_loopb_tail before being delivered back to the ip port.

ip_loopb_event is an event that has been placed in the system-wide event queue.


struct ip_fd *ip_proto_any:
struct ip_fd *ip_proto[IP_PROTO_HASH_NR]:


For a description of ip_proto_any and ip_proto[], click here.


0035036 PUBLIC ip_fd_t ip_fd_table[IP_FD_NR];
ip_fd / ip_fd_table[]

If a process opens up an ip device file (e.g., /dev/ip), an "ip file descriptor" is acquired to handle all read, write, and ioctl operations. Also, during the initialization of the higher-layer protocols (e.g., udp), an ip file descriptor is acquired to handle the read, write, and ioctl operations from the higher-layer port (e.g., udp_port). The relationships between various ports and file descriptors are shown in this figure:



ip file descriptors are ip_fd struct's. ip_fd_table[] is an array of all the ip file descriptors.

typedef struct ip_fd

{
int if_flags;
struct nwio_ipopt if_ipopt;
ip_port_t *if_port;
struct ip_fd *if_proto_next;
int if_srfd;
acc_t *if_rdbuf_head;
acc_t *if_rdbuf_tail;
get_userdata_t if_get_userdata;
put_userdata_t if_put_userdata;
put_pkt_t if_put_pkt;
time_t if_exp_time;
size_t if_rd_count;
} ip_fd_t;


typedef struct nwio_ipopt
{
u32_t nwio_flags;
ipaddr_t nwio_rem;
ip_hdropt_t nwio_hdropt;
u8_t nwio_tos;
u8_t nwio_ttl;
u8_t nwio_df;
ipproto_t nwio_proto;
} nwio_ipopt_t;


typedef struct ip_hdropt
{
u8_t iho_opt_siz;
u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE];
} ip_hdropt_t;
int if_flags:

#define IFF_EMPTY 0x0
#define IFF_INUSE 0x1
#define IFF_OPTSET 0x2
#define IFF_BUSY 0xC
#define IFF_READ_IP 0x4
#define IFF_GIPCONF_IP 0x8

During the initialization of ip_fd_table[], each ip_fd is set to IFF_EMPTY. After the ip file descriptor has been allocated, IFF_INUSE is set to indicate that the ip file descriptor is no longer available. And after the ip file descriptor has been configured, IFF_OPTSET is set. The ip file descriptor cannot be used until it has been configured. Later, when a higher layer (e.g., udp) wants to read from the ip file descriptor, the IFF_READ_IP flag is set.


struct nwio_ipopt if_ipopt: (fields shown below)


ip_port_t *if_port:

if_port points to the ip file descriptor's associated ip port. For example, if udp_port_table[0] is being initialized, ip_open() returns an ip file descriptor. if_port of this ip file descriptor will point to ip_port_table[0].


struct ip_fd *if_proto_next:

Points to the next ip file descriptor in the ip_proto[] or ip_proto_any linked lists.


int if_srfd:

if_srfd is the port number/file descriptor of the layer immediately above the ip layer. For example, if udp opens up this file descriptor, if_srfd will be the port number of the port that opened up this ip file descriptor. If, on the other hand, this file descriptor is opened up directly by the user (i.e., the user opens up /dev/ip instead of /dev/tcp or /dev/up), if_srfd will be the corresponding slot in sr_fd_table[]. Since if_srfd could be either a port or a file descriptor (fd), if_srfd is probably not the best name for this field.


acc_t *if_rdbuf_head:
acc_t *if_rdbuf_tail:


if_rdbuf_head is the head of a linked list of buffers (acc_t struct's) that are waiting to be read by a higher layer (e.g., udp). The acc_t struct's are linked together by the acc_ext_link field.


get_userdata_t if_get_userdata:
put_userdata_t if_put_userdata:
put_pkt_t if_put_pkt:


The three fields above are all pointers to functions that move packets and configuration data from one layer to the next. For example, if the udp code opens up an ip file descriptor, if_get_userdata, if_put_userdata, and if_put_pkt are set to udp_get_data(), udp_put_data(), and udp_ip_arrived().

These three functions are used to move packets and configuration data to and from the layer above (e.g., udp).


time_t if_exp_time:

Only a certain amount of time can pass from the time a packet is placed in the ip file descriptor's read queue until the packet is read. if_exp_time is this expiration time for the packet. If the packet is not read, the packet and all the later packets in the queue are deleted.


size_t if_rd_count:

The number of bytes to be read. if_rd_count is set in ip_read(). If the udp code opened up the ip file descriptor, this if_rd_count is always UDP_MAX_DATAGRAM.


u32_t nwio_flags:

nwio_flags will be a combination of the flags below. Most of the flags within a set are exclusionary. For example, both NWIO_REMSPEC and NWIO_REMANY can't both be set.

Note that "EN" stands for "ENable" and "DI" stands for "DIsable".

#define NWIO_EXCL 0x00000001l
#define NWIO_SHARED 0x00000002l
#define NWIO_COPY 0x00000003l

From ip(4):

"The options covered by NWIO_ACC_MASK control the number of channels that can use one IP protocol. If NWIO_EXCL is specified then only that channel can use a certain IP protocol. If NWIO_SHARED then multiple channels that all have to specify NWIO_SHARED can use the same IP protocol, but incoming packets will be delivered to a most one channel. NWIO_COPY does not impose any restrictions. Every channel gets a copy of an incoming packet."

Note that, for whatever reason, NWIO_EXCL behaves exactly as NWIO_COPY. Every channel receives a copy of an incoming packet.

The access flags are important during the read of an ip packet.

#define NWIO_EN_LOC 0x00000010l
#define NWIO_DI_LOC 0x00100000l
#define NWIO_EN_BROAD 0x00000020l
#define NWIO_DI_BROAD 0x00200000l

NWIO_EN_LOC specifies that this file descriptor can receive packets destined for this machine and NWIO_EN_BROAD specifies that this file descriptor can receive broadcast packets.

#define NWIO_REMSPEC 0x00000100l
#define NWIO_REMANY 0x01000000l

If the NWIO_REMANY flag is set, this file descriptor can send packets to any destination. If, on the other hand, the NWIO_REMSPEC flag is set, this file descriptor can only communicate with a single host. This host is specified by nwio_rem (see below).

#define NWIO_PROTOSPEC 0x00000200l
#define NWIO_PROTOANY 0x02000000l

If NWIO_PROTOANY is set, the ip file descriptor will accept packets of any protocol type. However, if NWIO_PROTOSPEC is set, only packets with a protocol type of nwio_proto (see below) are accepted.

#define NWIO_HDR_O_SPEC 0x00000400l
#define NWIO_HDR_O_ANY 0x04000000l

If the NWIO_HDR_O_SPEC flag in nwio_flags is set, nwio_hdropt (see below) must be set. If this is the case, the extra header information for all outgoing packets will be taken from nwio_hdropt, nwio_tos, nwio_ttl, and nwio_df (see below).

#define NWIO_RWDATONLY 0x00001000l
#define NWIO_RWDATALL 0x10000000l

NWIO_RWDATALL is a little tricky. If the NWIO_RWDATALL flag is set, the header was omitted when passing the packet down to ip code and the NWIO_EN_LOC, NWIO_DI_BROAD, NWIO_REMSPEC, NWIO_PROTOSPEC and NWIO_HDR_O_SPEC flags must all be set (and NWIO_REMANY and NWIO_PROTOANY cannot be set). In other words, this file descriptor can only send the data to one destination using one protocol.

During the configuration of an ip file descriptor being opened by the udp code, ip_ioctl() calls udp_get_data() (indirectly) to get configuration information. udp_get_data() returns the following configuration information:

NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | NWIO_HDR_O_ANY | NWIO_RWDATALL

ipaddr_t nwio_rem:

If the NWIO_REMSPEC flag in nwio_flags is set (see above), nwio_rem is the ip address of the destination host.


ip_hdropt_t nwio_hdropt:

The ip header length is flexible to allow for extra options. For example, in addition to the normal fields (e.g., destination ip address), an ip header may specify the route it wishes to take or request that the route be recorded.

that it wishes to record a route or to ip_chk_hdropt().


u8_t nwio_tos:

"tos" stands for "Type Of Service". nwio_tos is initialized to 0 but can be changed by ip_ioctl().


u8_t nwio_ttl:

"ttl" stands for "Time To Live", which is the number of hops that a packet can take before being dropped by a router. nwio_ttl is initialized to 255 but can be changed by ip_ioctl().


u8_t nwio_df:

nwio_df specifies whether fragmentation is allowed or not. nwio_df is initialized to FALSE but, again, can be changed by ip_ioctl().


ipproto_t nwio_proto:

nwio_proto can take one of the values below. Obviously, if the udp code opens up an ip file descriptor, nwio_proto will be IPPROTO_UDP. The same is true for icmp and tcp.

#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17

This field is used in conjunction with the NWIO_PROTOSPEC flag in nwio_flags. If this flag is set, nwio_proto must be set.


ip_hdropt: If the NWIO_HDR_O_SPEC flag is set (see above), any outgoing packets will have ip options as specified by iho_data[]. The length of the options will then be iho_opt_siz.

u8_t iho_opt_siz: The length of the options.

u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE]: The actual options.


0035037 PUBLIC ip_ass_t ip_ass_table[IP_ASS_NR];
find_ass_ent() / ip_ass_table[]

If an ip packet is fragmented, ip_ass_table[] (the ip assemble table) holds the fragments until they are reassembled by reassemble(). Each of the 3 elements (which is an oddly small number) of ip_ass_table[] corresponds to a packet that has been fragmented and is of type ip_ass_t (see below). find_ass_ent() searches through ip_ass_table[] for fragments of the same packet and adds the fragment to this packet if found and starts a new packet otherwise. If ip_ass_table[] is full, the oldest fragmented packet is dropped and replaced by the new fragmented packet and an icmp packet is sent to the source of the dropped packet.

typedef struct ip_ass

{
acc_t *ia_frags;
int ia_min_ttl;
ip_port_t *ia_port;
time_t ia_first_time;
ipaddr_t ia_srcaddr, ia_dstaddr;
int ia_proto, ia_id;
} ip_ass_t;
acc_t *ia_frags: The first fragment in the linked list of fragments. These accessors hold the data contained in the fragments and are linked by the accessors' acc_ext_link field.

int ia_min_ttl: Set to IP_MAX_TTL (#define'd as 255 in in.h). This value is in seconds and is the maximum time that a fragmented packet may be in ip_ass_table[] before the source is sent an icmp packet.

ip_port_t *ia_port: The ip port on which the packet arrived.

time_t ia_first_time: The time at which the first fragment of the packet is added.

ipaddr_t ia_srcaddr, ia_dstaddr: The source and destination ip address of the fragment.

int ia_proto: The protocol of the packet to which the fragment belongs. For example, if the packet is a udp packet, ia_proto will be 17. If the packet is a tcp packet, ia_proto will be 6.

ia_id: The value of the ih_id field for the ip header of the first packet sent out is determined by ip_init() and is equal to the number of clock ticks since reboot (i.e., the value returned by get_time) and is incremented for each packet sent out. This value is used to combine fragments at the receiving end if fragmentation has occurred. More specifically, if a packet is fragmented during transit, ia_id will be the same for all the fragments.



0035038 
0035039 PUBLIC void ip_prep()
ip_prep()

ip_prep() allocates memory for ip_port_table[] before calling icmp_prep().

ip_prep() is called a single time during the initialization of the network service. ip_prep() is called by nw_init().


0035040 {
0035041          ip_port_table= alloc(ip_conf_nr * sizeof(ip_port_table[0]));
ip_conf is the number of entries in the inet.conf file. For example, for the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

ip_conf will be 2. ip_conf was calculated in read_conf().


ip_port / ip_port_table[]


For every interface listed in inet.conf, there is a single ip port. For example, for the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

there will be an ip port associated with the ethernet interface and an ip port associated with the psip interface. Each of these ip ports is a struct ip_port (see below) and each ip_port struct is in ip_port_table[]. So, for the example inet.conf file above, ip_port_table[] will have 2 elements; ip_port_table[0] will be for the ethernet interface and ip_port_table[1] will be for the psip interface.

Each element in ip_port_table[] is associated with several ip file descriptors. For example, the udp code (during initialization) will open up an ip file descriptor and this ip file descriptor will be associated with one of the elements in ip_port_table[].



typedef struct ip_port

{
int ip_flags, ip_dl_type;
int ip_port;
union
{
struct
{
int de_state;
int de_flags;
int de_port;
int de_fd;
acc_t *de_frame;
acc_t *de_q_head;
acc_t *de_q_tail;
acc_t *de_arp_head;
acc_t *de_arp_tail;
} dl_eth;
struct
{
int ps_port;
acc_t *ps_send_head;
acc_t *ps_send_tail;
} dl_ps;
} ip_dl;
ipaddr_t ip_ipaddr;
ipaddr_t ip_netmask;
ipaddr_t ip_subnetmask;
u16_t ip_frame_id;
u16_t ip_mss;
ip_dev_t ip_dev_main;
ip_dev_t ip_dev_set_ipaddr;
ip_dev_send_t ip_dev_send;
acc_t *ip_loopb_head;
acc_t *ip_loopb_tail;
event_t ip_loopb_event;
struct ip_fd *ip_proto_any;
struct ip_fd *ip_proto[IP_PROTO_HASH_NR];
} ip_port_t;



int ip_flags:

The possible ip_flags are #define'd in ip_int.h:

#define IPF_EMPTY 0x0
#define IPF_CONFIGURED 0x1
#define IPF_IPADDRSET 0x2
#define IPF_NETMASKSET 0x4

After the initialization of the ip port, ip_flags is set to IPF_CONFIGURED. If the "ifconfig -h host-IP-address" command is issued, ip_ioctl() sets the IPF_IPADDRSET flag before setting the ip address and (optionally) the subnet mask.


int ip_dl_type:

"dl" stands for "data link" (layer). ip_dl_type is set to the corresponding data link layer type of the port. These types include NETTYPE_ETH (ethernet) and NETTYPE_PSIP (psip).


int ip_port:

The port number of the ip device. For example, for a system with the following /etc/inet.conf file:

eth0 DP8390 0 { default; };
psip1;

there will be 2 ports: port 0 for the ethernet device and port 1 for the psip device.

Note that this port will not necessarily be the same as dl_eth.de_port (see below).


struct dl_eth: The dl_eth struct is used (instead of dl_ps) if the underlying data link layer device of this port is an ethernet device.


int de_state:

The possible de_state values are #define'd in ip_int.h:

#define IES_EMPTY 0x0
#define IES_SETPROTO 0x1
#define IES_GETIPADDR 0x2
#define IES_MAIN 0x3
#define IES_ERROR 0x4

When the ip port is being initialized, de_state changes in quick succession from IES_EMPTY to IES_SETPROTO to IES_GETIPADDR before entering IES_MAIN, which is its normal operational state.


int de_flags:

de_flags is initialized to IEF_EMPTY. Note that "SP" stands for "SusPend".

#define IEF_EMPTY 0x1
#define IEF_SUSPEND 0x8
#define IEF_READ_IP 0x10
#define IEF_READ_SP 0x20
#define IEF_WRITE_SP 0x80


int de_port:

The ethernet port number. For example, if there were two ethernet devices, one ethernet device would have port 0 and the other would have port 1, regardless of how many psip devices were on the system.

This value is initialized in ip_init(). Also see the initial comments in ip_config.c for a description of ip_conf[].

Note that this port will not necessarily be the same as ip_port (see above).


int de_fd:

Initialized by calling eth_open(), de_fd is the ip port's associated ethernet file descriptor.


acc_t *de_frame:
acc_t *de_q_head:
acc_t *de_q_tail:


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 an ethernet port's 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 the ip port to send off to the ethernet port, the ip port encapsulates the ip packet with an ethernet header and the resulting ethernet packet is placed in the dl_eth.de_frame field of the ip port. If the ip port 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 *de_arp_head:
acc_t *de_arp_tail:


If the arp table (i.e., arp cache) does not contain an entry for a given ip address, arp_ip_eth() returns NW_SUSPEND and the outgoing packet is placed in the ip port's de_arp_head/de_arp_tail queue. Before being placed in this queue, the packet is encapsulated in a "xmit" header (as opposed to an ethernet header).

xmit_hdr is declared in generic/ip_eth.c:

typedef struct xmit_hdr

{
time_t xh_time;
ipaddr_t xh_ipaddr;
} xmit_hdr_t;
where xh_time is the time at which the packet is placed in the de_arp_head/de_arp_tail queue and xh_ipaddr is the destination ip address of the packet.


struct dl_ps:
int ps_port:
acc_t *ps_send_head:
acc_t *ps_send_tail:


The dl_ps struct is used (instead of dl_eth) if the underlying data link layer device is a psip device. Coverage of psip is not included in this documentation.


ipaddr_t ip_ipaddr:
ipaddr_t ip_netmask:
ipaddr_t ip_subnetmask:


The ip address of a port can be set in two ways, either with RARP or through the "ifconfig -h host-IP-address" command. If set by the ifconfig command, a message requesting an NWIOSIPCONF (Set IP CONFiguration) is sent to the appropriate ip device (e.g., /dev/ip00, causing ip_ioctl() to be called. The user then passes in a nwio_ipconf struct which contains either the ip address or the subnet mask or both.

The netmask is simply a reflection of the class to which the ip address belongs. For example, if the ip address is 194.77.33.5, then it is a class C address and its netmask is therefore 255.255.255.0. See ip_nettype() for more information.


u16_t ip_frame_id:

ip_frame_id is initialized to the time at which the ip port was configured and incremented each time a packet is sent out. The ih_id field of each packet's ip header is set to ip_frame_id. If a packet is fragmented, the receiver can properly order the framents.


u16_t ip_mss:

If the ip_port_table[] element has an underlying ethernet layer, ip_mss is initialized to ETH_MAX_PACK_SIZE-ETH_HDR_SIZE (1514-14=1500), the size of the payload of an ethernet packet in bytes. If the size of the resulting ip packet is too large, the code fragments the packet.


ip_dev_t ip_dev_main:

ip_dev_main is initialized to ipeth_main() for ethernet devices. This function is called in ip_init().


ip_dev_t ip_dev_set_ipaddr:

ip_dev_set_ipaddr is initialized to ipeth_set_ipaddr() for ethernet devices.


ip_dev_send_t ip_dev_send:

ip_dev_send is initialized to ipeth_send() for ethernet devices or ipps_send() for psip devices.


acc_t *ip_loopb_head:
acc_t *ip_loopb_tail:
event_t ip_loopb_event:


Ip packets destined for the loopback address (127.0.0.1) or destined for the ip address of the ip port itself are placed in the ip_loopb_head/ip_loopb_tail before being delivered back to the ip port.

ip_loopb_event is an event that has been placed in the system-wide event queue.


struct ip_fd *ip_proto_any:
struct ip_fd *ip_proto[IP_PROTO_HASH_NR]:


For a description of ip_proto_any and ip_proto[], click here.


0035042          icmp_prep();
icmp_prep()

icmp_prep() simply allocates space for icmp_port_table[].


0035043 }
0035044 
0035045 PUBLIC void ip_init()
ip_init()

ip_init() initializes ip_ass_table[] (the fragmentation table), ip_fd_table[] (the ip file descriptor table), and ip_port_table[] (the ip port table).

ip_init() also calls icmp_init() (icmp initialization) and ipr_init() (router initialization).

ip_init() is called a single time during the initialization of the network service. ip_init() is called by nw_init().


0035046 {
0035047          int i, j, result;
0035048          ip_ass_t *ip_ass;
0035049          ip_fd_t *ip_fd;
0035050          ip_port_t *ip_port;
0035051          struct ip_conf *icp;
0035052 
0035053          assert (BUF_S >= sizeof(struct nwio_ethopt));
0035054          assert (BUF_S >= IP_MAX_HDR_SIZE + ETH_HDR_SIZE);
0035055          assert (BUF_S >= sizeof(nwio_ipopt_t));
0035056          assert (BUF_S >= sizeof(nwio_route_t));
0035057 
0035058 #if ZERO
0035059          for (i=0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++)
Initialize ip_ass_table[].


find_ass_ent() / ip_ass_table[]


If an ip packet is fragmented, ip_ass_table[] (the ip assemble table) holds the fragments until they are reassembled by reassemble(). Each of the 3 elements (which is an oddly small number) of ip_ass_table[] corresponds to a packet that has been fragmented and is of type ip_ass_t (see below). find_ass_ent() searches through ip_ass_table[] for fragments of the same packet and adds the fragment to this packet if found and starts a new packet otherwise. If ip_ass_table[] is full, the oldest fragmented packet is dropped and replaced by the new fragmented packet and an icmp packet is sent to the source of the dropped packet.

typedef struct ip_ass

{
acc_t *ia_frags;
int ia_min_ttl;
ip_port_t *ia_port;
time_t ia_first_time;
ipaddr_t ia_srcaddr, ia_dstaddr;
int ia_proto, ia_id;
} ip_ass_t;
acc_t *ia_frags: The first fragment in the linked list of fragments. These accessors hold the data contained in the fragments and are linked by the accessors' acc_ext_link field.

int ia_min_ttl: Set to IP_MAX_TTL (#define'd as 255 in in.h). This value is in seconds and is the maximum time that a fragmented packet may be in ip_ass_table[] before the source is sent an icmp packet.

ip_port_t *ia_port: The ip port on which the packet arrived.

time_t ia_first_time: The time at which the first fragment of the packet is added.

ipaddr_t ia_srcaddr, ia_dstaddr: The source and destination ip address of the fragment.

int ia_proto: The protocol of the packet to which the fragment belongs. For example, if the packet is a udp packet, ia_proto will be 17. If the packet is a tcp packet, ia_proto will be 6.

ia_id: The value of the ih_id field for the ip header of the first packet sent out is determined by ip_init() and is equal to the number of clock ticks since reboot (i.e., the value returned by get_time) and is incremented for each packet sent out. This value is used to combine fragments at the receiving end if fragmentation has occurred. More specifically, if a packet is fragmented during transit, ia_id will be the same for all the fragments.



0035060          {
0035061                   ip_ass->ia_frags= 0;
0035062                   ip_ass->ia_first_time= 0;
0035063                   ip_ass->ia_port= 0;
0035064          }
0035065 
0035066          for (i=0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
Initialize ip_fd_table[].


ip_fd / ip_fd_table[]


If a process opens up an ip device file (e.g., /dev/ip), an "ip file descriptor" is acquired to handle all read, write, and ioctl operations. Also, during the initialization of the higher-layer protocols (e.g., udp), an ip file descriptor is acquired to handle the read, write, and ioctl operations from the higher-layer port (e.g., udp_port). The relationships between various ports and file descriptors are shown in this figure:



ip file descriptors are ip_fd struct's. ip_fd_table[] is an array of all the ip file descriptors.

typedef struct ip_fd

{
int if_flags;
struct nwio_ipopt if_ipopt;
ip_port_t *if_port;
struct ip_fd *if_proto_next;
int if_srfd;
acc_t *if_rdbuf_head;
acc_t *if_rdbuf_tail;
get_userdata_t if_get_userdata;
put_userdata_t if_put_userdata;
put_pkt_t if_put_pkt;
time_t if_exp_time;
size_t if_rd_count;
} ip_fd_t;


typedef struct nwio_ipopt
{
u32_t nwio_flags;
ipaddr_t nwio_rem;
ip_hdropt_t nwio_hdropt;
u8_t nwio_tos;
u8_t nwio_ttl;
u8_t nwio_df;
ipproto_t nwio_proto;
} nwio_ipopt_t;


typedef struct ip_hdropt
{
u8_t iho_opt_siz;
u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE];
} ip_hdropt_t;
int if_flags:

#define IFF_EMPTY 0x0
#define IFF_INUSE 0x1
#define IFF_OPTSET 0x2
#define IFF_BUSY 0xC
#define IFF_READ_IP 0x4
#define IFF_GIPCONF_IP 0x8

During the initialization of ip_fd_table[], each ip_fd is set to IFF_EMPTY. After the ip file descriptor has been allocated, IFF_INUSE is set to indicate that the ip file descriptor is no longer available. And after the ip file descriptor has been configured, IFF_OPTSET is set. The ip file descriptor cannot be used until it has been configured. Later, when a higher layer (e.g., udp) wants to read from the ip file descriptor, the IFF_READ_IP flag is set.


struct nwio_ipopt if_ipopt: (fields shown below)


ip_port_t *if_port:

if_port points to the ip file descriptor's associated ip port. For example, if udp_port_table[0] is being initialized, ip_open() returns an ip file descriptor. if_port of this ip file descriptor will point to ip_port_table[0].


struct ip_fd *if_proto_next:

Points to the next ip file descriptor in the ip_proto[] or ip_proto_any linked lists.


int if_srfd:

if_srfd is the port number/file descriptor of the layer immediately above the ip layer. For example, if udp opens up this file descriptor, if_srfd will be the port number of the port that opened up this ip file descriptor. If, on the other hand, this file descriptor is opened up directly by the user (i.e., the user opens up /dev/ip instead of /dev/tcp or /dev/up), if_srfd will be the corresponding slot in sr_fd_table[]. Since if_srfd could be either a port or a file descriptor (fd), if_srfd is probably not the best name for this field.


acc_t *if_rdbuf_head:
acc_t *if_rdbuf_tail:


if_rdbuf_head is the head of a linked list of buffers (acc_t struct's) that are waiting to be read by a higher layer (e.g., udp). The acc_t struct's are linked together by the acc_ext_link field.


get_userdata_t if_get_userdata:
put_userdata_t if_put_userdata:
put_pkt_t if_put_pkt:


The three fields above are all pointers to functions that move packets and configuration data from one layer to the next. For example, if the udp code opens up an ip file descriptor, if_get_userdata, if_put_userdata, and if_put_pkt are set to udp_get_data(), udp_put_data(), and udp_ip_arrived().

These three functions are used to move packets and configuration data to and from the layer above (e.g., udp).


time_t if_exp_time:

Only a certain amount of time can pass from the time a packet is placed in the ip file descriptor's read queue until the packet is read. if_exp_time is this expiration time for the packet. If the packet is not read, the packet and all the later packets in the queue are deleted.


size_t if_rd_count:

The number of bytes to be read. if_rd_count is set in ip_read(). If the udp code opened up the ip file descriptor, this if_rd_count is always UDP_MAX_DATAGRAM.


u32_t nwio_flags:

nwio_flags will be a combination of the flags below. Most of the flags within a set are exclusionary. For example, both NWIO_REMSPEC and NWIO_REMANY can't both be set.

Note that "EN" stands for "ENable" and "DI" stands for "DIsable".

#define NWIO_EXCL 0x00000001l
#define NWIO_SHARED 0x00000002l
#define NWIO_COPY 0x00000003l

From ip(4):

"The options covered by NWIO_ACC_MASK control the number of channels that can use one IP protocol. If NWIO_EXCL is specified then only that channel can use a certain IP protocol. If NWIO_SHARED then multiple channels that all have to specify NWIO_SHARED can use the same IP protocol, but incoming packets will be delivered to a most one channel. NWIO_COPY does not impose any restrictions. Every channel gets a copy of an incoming packet."

Note that, for whatever reason, NWIO_EXCL behaves exactly as NWIO_COPY. Every channel receives a copy of an incoming packet.

The access flags are important during the read of an ip packet.

#define NWIO_EN_LOC 0x00000010l
#define NWIO_DI_LOC 0x00100000l
#define NWIO_EN_BROAD 0x00000020l
#define NWIO_DI_BROAD 0x00200000l

NWIO_EN_LOC specifies that this file descriptor can receive packets destined for this machine and NWIO_EN_BROAD specifies that this file descriptor can receive broadcast packets.

#define NWIO_REMSPEC 0x00000100l
#define NWIO_REMANY 0x01000000l

If the NWIO_REMANY flag is set, this file descriptor can send packets to any destination. If, on the other hand, the NWIO_REMSPEC flag is set, this file descriptor can only communicate with a single host. This host is specified by nwio_rem (see below).

#define NWIO_PROTOSPEC 0x00000200l
#define NWIO_PROTOANY 0x02000000l

If NWIO_PROTOANY is set, the ip file descriptor will accept packets of any protocol type. However, if NWIO_PROTOSPEC is set, only packets with a protocol type of nwio_proto (see below) are accepted.

#define NWIO_HDR_O_SPEC 0x00000400l
#define NWIO_HDR_O_ANY 0x04000000l

If the NWIO_HDR_O_SPEC flag in nwio_flags is set, nwio_hdropt (see below) must be set. If this is the case, the extra header information for all outgoing packets will be taken from nwio_hdropt, nwio_tos, nwio_ttl, and nwio_df (see below).

#define NWIO_RWDATONLY 0x00001000l
#define NWIO_RWDATALL 0x10000000l

NWIO_RWDATALL is a little tricky. If the NWIO_RWDATALL flag is set, the header was omitted when passing the packet down to ip code and the NWIO_EN_LOC, NWIO_DI_BROAD, NWIO_REMSPEC, NWIO_PROTOSPEC and NWIO_HDR_O_SPEC flags must all be set (and NWIO_REMANY and NWIO_PROTOANY cannot be set). In other words, this file descriptor can only send the data to one destination using one protocol.

During the configuration of an ip file descriptor being opened by the udp code, ip_ioctl() calls udp_get_data() (indirectly) to get configuration information. udp_get_data() returns the following configuration information:

NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | NWIO_HDR_O_ANY | NWIO_RWDATALL

ipaddr_t nwio_rem:

If the NWIO_REMSPEC flag in nwio_flags is set (see above), nwio_rem is the ip address of the destination host.


ip_hdropt_t nwio_hdropt:

The ip header length is flexible to allow for extra options. For example, in addition to the normal fields (e.g., destination ip address), an ip header may specify the route it wishes to take or request that the route be recorded.

that it wishes to record a route or to ip_chk_hdropt().


u8_t nwio_tos:

"tos" stands for "Type Of Service". nwio_tos is initialized to 0 but can be changed by ip_ioctl().


u8_t nwio_ttl:

"ttl" stands for "Time To Live", which is the number of hops that a packet can take before being dropped by a router. nwio_ttl is initialized to 255 but can be changed by ip_ioctl().


u8_t nwio_df:

nwio_df specifies whether fragmentation is allowed or not. nwio_df is initialized to FALSE but, again, can be changed by ip_ioctl().


ipproto_t nwio_proto:

nwio_proto can take one of the values below. Obviously, if the udp code opens up an ip file descriptor, nwio_proto will be IPPROTO_UDP. The same is true for icmp and tcp.

#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17

This field is used in conjunction with the NWIO_PROTOSPEC flag in nwio_flags. If this flag is set, nwio_proto must be set.


ip_hdropt: If the NWIO_HDR_O_SPEC flag is set (see above), any outgoing packets will have ip options as specified by iho_data[]. The length of the options will then be iho_opt_siz.

u8_t iho_opt_siz: The length of the options.

u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE]: The actual options.


0035067          {
0035068                   ip_fd->if_flags= IFF_EMPTY;
0035069                   ip_fd->if_rdbuf_head= 0;
0035070          }
0035071 #endif
0035072 
0035073          for (i=0, ip_port= ip_port_table, icp= ip_conf;
0035074                   i<ip_conf_nr; i++, ip_port++, icp++)
Initialize ip_port_table[]. The initialization of each element of ip_port_table[] is dependent on the underlying layer, which will be either ethernet or psip.


ip_port / ip_port_table[]


For every interface listed in inet.conf, there is a single ip port. For example, for the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

there will be an ip port associated with the ethernet interface and an ip port associated with the psip interface. Each of these ip ports is a struct ip_port (see below) and each ip_port struct is in ip_port_table[]. So, for the example inet.conf file above, ip_port_table[] will have 2 elements; ip_port_table[0] will be for the ethernet interface and ip_port_table[1] will be for the psip interface.

Each element in ip_port_table[] is associated with several ip file descriptors. For example, the udp code (during initialization) will open up an ip file descriptor and this ip file descriptor will be associated with one of the elements in ip_port_table[].



typedef struct ip_port

{
int ip_flags, ip_dl_type;
int ip_port;
union
{
struct
{
int de_state;
int de_flags;
int de_port;
int de_fd;
acc_t *de_frame;
acc_t *de_q_head;
acc_t *de_q_tail;
acc_t *de_arp_head;
acc_t *de_arp_tail;
} dl_eth;
struct
{
int ps_port;
acc_t *ps_send_head;
acc_t *ps_send_tail;
} dl_ps;
} ip_dl;
ipaddr_t ip_ipaddr;
ipaddr_t ip_netmask;
ipaddr_t ip_subnetmask;
u16_t ip_frame_id;
u16_t ip_mss;
ip_dev_t ip_dev_main;
ip_dev_t ip_dev_set_ipaddr;
ip_dev_send_t ip_dev_send;
acc_t *ip_loopb_head;
acc_t *ip_loopb_tail;
event_t ip_loopb_event;
struct ip_fd *ip_proto_any;
struct ip_fd *ip_proto[IP_PROTO_HASH_NR];
} ip_port_t;



int ip_flags:

The possible ip_flags are #define'd in ip_int.h:

#define IPF_EMPTY 0x0
#define IPF_CONFIGURED 0x1
#define IPF_IPADDRSET 0x2
#define IPF_NETMASKSET 0x4

After the initialization of the ip port, ip_flags is set to IPF_CONFIGURED. If the "ifconfig -h host-IP-address" command is issued, ip_ioctl() sets the IPF_IPADDRSET flag before setting the ip address and (optionally) the subnet mask.


int ip_dl_type:

"dl" stands for "data link" (layer). ip_dl_type is set to the corresponding data link layer type of the port. These types include NETTYPE_ETH (ethernet) and NETTYPE_PSIP (psip).


int ip_port:

The port number of the ip device. For example, for a system with the following /etc/inet.conf file:

eth0 DP8390 0 { default; };
psip1;

there will be 2 ports: port 0 for the ethernet device and port 1 for the psip device.

Note that this port will not necessarily be the same as dl_eth.de_port (see below).


struct dl_eth: The dl_eth struct is used (instead of dl_ps) if the underlying data link layer device of this port is an ethernet device.


int de_state:

The possible de_state values are #define'd in ip_int.h:

#define IES_EMPTY 0x0
#define IES_SETPROTO 0x1
#define IES_GETIPADDR 0x2
#define IES_MAIN 0x3
#define IES_ERROR 0x4

When the ip port is being initialized, de_state changes in quick succession from IES_EMPTY to IES_SETPROTO to IES_GETIPADDR before entering IES_MAIN, which is its normal operational state.


int de_flags:

de_flags is initialized to IEF_EMPTY. Note that "SP" stands for "SusPend".

#define IEF_EMPTY 0x1
#define IEF_SUSPEND 0x8
#define IEF_READ_IP 0x10
#define IEF_READ_SP 0x20
#define IEF_WRITE_SP 0x80


int de_port:

The ethernet port number. For example, if there were two ethernet devices, one ethernet device would have port 0 and the other would have port 1, regardless of how many psip devices were on the system.

This value is initialized in ip_init(). Also see the initial comments in ip_config.c for a description of ip_conf[].

Note that this port will not necessarily be the same as ip_port (see above).


int de_fd:

Initialized by calling eth_open(), de_fd is the ip port's associated ethernet file descriptor.


acc_t *de_frame:
acc_t *de_q_head:
acc_t *de_q_tail:


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 an ethernet port's 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 the ip port to send off to the ethernet port, the ip port encapsulates the ip packet with an ethernet header and the resulting ethernet packet is placed in the dl_eth.de_frame field of the ip port. If the ip port 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 *de_arp_head:
acc_t *de_arp_tail:


If the arp table (i.e., arp cache) does not contain an entry for a given ip address, arp_ip_eth() returns NW_SUSPEND and the outgoing packet is placed in the ip port's de_arp_head/de_arp_tail queue. Before being placed in this queue, the packet is encapsulated in a "xmit" header (as opposed to an ethernet header).

xmit_hdr is declared in generic/ip_eth.c:

typedef struct xmit_hdr

{
time_t xh_time;
ipaddr_t xh_ipaddr;
} xmit_hdr_t;
where xh_time is the time at which the packet is placed in the de_arp_head/de_arp_tail queue and xh_ipaddr is the destination ip address of the packet.


struct dl_ps:
int ps_port:
acc_t *ps_send_head:
acc_t *ps_send_tail:


The dl_ps struct is used (instead of dl_eth) if the underlying data link layer device is a psip device. Coverage of psip is not included in this documentation.


ipaddr_t ip_ipaddr:
ipaddr_t ip_netmask:
ipaddr_t ip_subnetmask:


The ip address of a port can be set in two ways, either with RARP or through the "ifconfig -h host-IP-address" command. If set by the ifconfig command, a message requesting an NWIOSIPCONF (Set IP CONFiguration) is sent to the appropriate ip device (e.g., /dev/ip00, causing ip_ioctl() to be called. The user then passes in a nwio_ipconf struct which contains either the ip address or the subnet mask or both.

The netmask is simply a reflection of the class to which the ip address belongs. For example, if the ip address is 194.77.33.5, then it is a class C address and its netmask is therefore 255.255.255.0. See ip_nettype() for more information.


u16_t ip_frame_id:

ip_frame_id is initialized to the time at which the ip port was configured and incremented each time a packet is sent out. The ih_id field of each packet's ip header is set to ip_frame_id. If a packet is fragmented, the receiver can properly order the framents.


u16_t ip_mss:

If the ip_port_table[] element has an underlying ethernet layer, ip_mss is initialized to ETH_MAX_PACK_SIZE-ETH_HDR_SIZE (1514-14=1500), the size of the payload of an ethernet packet in bytes. If the size of the resulting ip packet is too large, the code fragments the packet.


ip_dev_t ip_dev_main:

ip_dev_main is initialized to ipeth_main() for ethernet devices. This function is called in ip_init().


ip_dev_t ip_dev_set_ipaddr:

ip_dev_set_ipaddr is initialized to ipeth_set_ipaddr() for ethernet devices.


ip_dev_send_t ip_dev_send:

ip_dev_send is initialized to ipeth_send() for ethernet devices or ipps_send() for psip devices.


acc_t *ip_loopb_head:
acc_t *ip_loopb_tail:
event_t ip_loopb_event:


Ip packets destined for the loopback address (127.0.0.1) or destined for the ip address of the ip port itself are placed in the ip_loopb_head/ip_loopb_tail before being delivered back to the ip port.

ip_loopb_event is an event that has been placed in the system-wide event queue.


struct ip_fd *ip_proto_any:
struct ip_fd *ip_proto[IP_PROTO_HASH_NR]:


For a description of ip_proto_any and ip_proto[], click here.


0035075          {
0035076                   ip_port->ip_port= i;
0035077 #if ZERO
0035078                   ip_port->ip_flags= IPF_EMPTY;
0035079 #endif
The following three fields will be set later with values appropriate for their underlying data link layers.


0035080                   ip_port->ip_dev_main= (ip_dev_t)ip_bad_callback;
0035081                   ip_port->ip_dev_set_ipaddr= (ip_dev_t)ip_bad_callback;
0035082                   ip_port->ip_dev_send= (ip_dev_send_t)ip_bad_callback;
0035083                   ip_port->ip_dl_type= icp->ic_devtype;
If the underlying data link layer is ethernet, ic_devtype will be NETTYPE_ETH and if the underlying data link layer is psip, ic_devtype will be NETTYPE_PSIP.


0035084                   ip_port->ip_mss= IP_DEF_MSS;
IP_DEF_MSS is #define'd in /include/net/gen/in.h:

#define IP_DEF_MSS 576

From RFC 879:

"The default IP Maximum Datagram Size is 576."

and

"Hosts must not send datagrams larger than 576 octets unless they have specific knowledge that the destination host is prepared to accept larger datagrams."

And from RFC 791:

"Every internet destination must be able to receive a datagram of 576 octets either in one piece or in fragments to be reassembled."

The value of ip_mss is changed to 1500 bytes if the underlying layer of the ip_port_table[] element is ethernet.


0035085 
0035086                   switch(ip_port->ip_dl_type)
0035087                   {
Call the initialization function appropriate for the underlying data link layer.


0035088                   case IPDL_ETH:
0035089                            ip_port->ip_dl.dl_eth.de_port= icp->ic_port;
ic_port is the ethernet port number. For example, if there were two ethernet devices, the first ethernet ethernet device list in inet.conf would be port 0 and the second would be port 1, regardless of how many psip devices were on the system.


0035090                            result= ipeth_init(ip_port);
ipeth_init()

If an ip port's underlying data-link layer is ethernet, ipeth_init() is called by ip_init() during the ip port's initialization. ipeth_init() calls eth_open() to acquire an ethernet file descriptor and then initializes several ethernet-dependent fields of the ip port (e.g., ip_dl.dl_eth.de_flags).


0035091                            if (result == -1)
0035092                                     continue;
0035093                            assert(result == NW_OK);
0035094                            break;
0035095 #if ENABLE_PSIP
0035096                   case IPDL_PSIP:
The psip data link layer is not covered in this documentation.


0035097                            ip_port->ip_dl.dl_ps.ps_port= icp->ic_port;
0035098                            result= ipps_init(ip_port);
0035099                            if (result == -1)
0035100                                     continue;
0035101                            assert(result == NW_OK);
0035102                            break;
0035103 #endif
0035104 #if !CRAMPED
0035105                   default:
Only the ethernet and psip data link layers are currently supported.


0035106                            ip_panic(( "unknown ip_dl_type %d",
0035107                                                        ip_port->ip_dl_type ));
0035108 #endif
0035109                   }
0035110 #if ZERO
0035111                   ip_port->ip_loopb_head= NULL;
0035112                   ip_port->ip_loopb_tail= NULL;
0035113                   ev_init(&ip_port->ip_loopb_event);
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.


0035114 #endif
0035115                   ip_port->ip_flags |= IPF_CONFIGURED;
0035116 #if ZERO
0035117                   ip_port->ip_proto_any= NULL;
0035118                   for (j= 0; j<IP_PROTO_HASH_NR; j++)
0035119                            ip_port->ip_proto[j]= NULL;
0035120 #endif
0035121          }
0035122 
0035123 #ifndef BUF_CONSISTENCY_CHECK
0035124          bf_logon(ip_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



0035125 #else
0035126          bf_logon(ip_buffree, ip_bufcheck);
0035127 #endif
0035128 
0035129          icmp_init();
icmp_init()

icmp_init() initializes icmp_port_table[] by setting a few fields for each icmp port and then (again for each icmp port) calls icmp_main() to complete the initialization.


0035130          ipr_init();
ipr_init()

ipr_init() simply marks all the entries in the input routing table and the output routing table as unused.


0035131 
0035132          for (i=0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++)
0035133          {
0035134                   if (!(ip_port->ip_flags & IPF_CONFIGURED))
0035135                            continue;
Skip any elements in ip_port_table[] that are not being used. For the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

only the 0th and 1st elements in ip_port_table[] are used. The 2nd and 3rd elements are not used.


0035136                   ip_port->ip_frame_id= (u16_t)get_time();
ip_frame_id is incremented for each packet sent out. If fragmentation occurs, this value is used to combine fragments at the receiving end.


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.


0035137 
0035138                   sr_add_minor(if2minor(ip_conf[i].ic_ifno, IP_DEV_OFF),
0035139                            i, ip_open, ip_close, ip_read,
0035140                            ip_write, ip_ioctl, ip_cancel);
sr_fd / sr_fd_table[] / sr_add_minor()

One of the most important data arrays in the network service is sr_fd_table[], an array of 64 struct sr_fd's. Each sr_fd element in sr_fd_table[] corresponds to either a device or an opened file descriptor to a device (i.e., a "channel"):

typedef struct sr_fd

{
int srf_flags;
int srf_fd;
int srf_port;
sr_open_t srf_open;
sr_close_t srf_close;
sr_write_t srf_write;
sr_read_t srf_read;
sr_ioctl_t srf_ioctl;
sr_cancel_t srf_cancel;
mq_t *srf_ioctl_q, *srf_ioctl_q_tail;
mq_t *srf_read_q, *srf_read_q_tail;
mq_t *srf_write_q, *srf_write_q_tail;
} sr_fd_t;
For each device (e.g., /dev/udp0), an element in sr_fd_table[] is configured by sr_add_minor(). For example, for the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

an element (i.e., a struct sr_fd) is configured for each of the following devices:

/dev/eth0 sr_fd_table[1]
/dev/ip0 sr_fd_table[2]
/dev/tcp0 sr_fd_table[3]
/dev/udp0 sr_fd_table[4]

/dev/psip1 sr_fd_table[17]
/dev/ip1 sr_fd_table[18]
/dev/tcp1 sr_fd_table[19]
/dev/udp1 sr_fd_table[20]




sr_add_minor() is called in the initialization routines for the various protocols: mnx_eth.c (osdep_eth_init()), psip.c (psip_enable()), ip.c (ip_init()), tcp.c (tcp_init()), and udp.c (udp_init()).



When a device file (e.g., /dev/udp0) is opened by a process, the element that corresponds to the device is copied to an element that is currently unoccupied (see sr_open()). In this way, a "channel" is opened. Using this technique, a channel can be opened, closed, and manipulated without affecting the elements of the descriptors initially set by sr_add_minor().


int srf_flags:

srf_flags is a combination of the following:

#define SFF_FREE 0x00
#define SFF_MINOR 0x01
#define SFF_INUSE 0x02
#define SFF_BUSY 0x3C
#define SFF_IOCTL_IP 0x04
#define SFF_READ_IP 0x08
#define SFF_WRITE_IP 0x10
#define SFF_PENDING_REQ 0x30
#define SFF_SUSPENDED 0x1C0
#define SFF_IOCTL_SUSP 0x40
#define SFF_READ_SUSP 0x80
#define SFF_WRITE_SUSP

srf_flags is initialized to SFF_FREE for each element in sr_fd_table[]. If the channel corresponds to a device file, srf_flags is set to SFF_INUSE | SFF_MINOR. If the channel does not correspond to a device file, srf_flags is set simply to SFF_INUSE.

When a request comes in for a read, write, or ioctl operation and the network service is not already processing another request for the same operation, srf_flags is set to SFF_READ_IP, SFF_WRITE_IP, or SFF_IOCTL_IP. However, if an operation is attempted but the underlying protocol is still processing a previous request of the same nature (e.g., udp_write()), the appropriate flag (SFF_IOCTL_SUSP, SFF_READ_SUSP, or SFF_WRITE_SUSP) in srf_flags is set.


int srf_fd, srf_port:

srf_fd and srf_port are both set by sr_add_minor(). For the channels in srf_fd_table[] that correspond to the device files (e.g., /dev/udp0), srf_fd is set to the minor device number of the device. For example, if /dev/udp0 is added to sr_fd_table[] and the interface number of the device file is 0 (see comments for ip_conf[]), then the minor device number is:

if2minor(ifno, dev) = ((0)*16 + UDP_DEV = 0 + 4 = 4

For the channels in srf_fd_table[] that do not correspond to a device file, srf_fd is the file descriptor for the appropriate protocol. For example, if the file system requests that a udp channel be opened, srf_open is dereferenced and udp_open() is called. udp_open() opens a udp file descriptor and returns the index of the corresponding element in udp_fd_table[]. srf_fd is set to the index of this element.

Later, when the file system requests a read or a write on the open channel, srf_fd is passed into the protocol-specific read or write function (e.g., udp_read()), allowing the protocol-specific function to locate the appropriate file descriptor (e.g., udp file descriptor).

srf_port is more straight-forward. srf_port is the index in the protocol's port table. For example, if a system has two udp device files (/dev/udp0 and /dev/udp1), udp_port_table[] will have two entries, 0 and 1. Therefore, srf_port for the entry in sr_fd_table[] that corresponds to /dev/udp0 will be 0 and srf_port for the entry that corresponds to /dev/udp1 will be 1.


sr_open_t srf_open:
sr_close_t srf_close:
sr_write_t srf_write:
sr_read_t srf_read:
sr_ioctl_t srf_ioctl:
sr_cancel_t srf_cancel:


The fields above are all protocol-specific functions and and are all set by sr_add_minor(). For example, when sr_add_minor() is called by udp_init(), srf_open, srf_close, srf_write, srf_read, srf_ioctl, and srf_cancel are set to the pointers of the functions udp_open(), udp_close(), udp_write(), udp_read(), udp_ioctl(), and udp_cancel(). Later, when the file system makes a request to the network service, these functions will be called. For example, if the file system requests that data is written to a channel, srf_write is dereferenced and, if the channel is a udp channel, udp_write() is called.

mq_t *srf_ioctl_q, *srf_ioctl_q_tail:
mq_t *srf_read_q, *srf_read_q_tail:
mq_t *srf_write_q, *srf_write_q_tail:


The fields above are linked lists of ioctl, read, and write messages waiting to be processed. When a message requesting an ioctl, read, or write operation is received, the message is placed at the end of the linked list (unless there are no previous messages of this type that have not already been processed).


After the initialization of the network service, sr_rec() is called upon receipt of messages from the file system in the endless loop within main(). sr_rec() then calls a function to handle the specific request. For open requests, sr_rec() calls sr_open(); for read, write, and io requests, sr_rec() calls sr_rwio(); for close requests, sr_rec() calls sr_close(); for cancel requests, sr_rec() calls sr_cancel().


0035141 
0035142                   (*ip_port->ip_dev_main)(ip_port);
If the ip port's underlying data link layer is ethernet, ip_dev_main was just initialized to ipeth_main by ipeth_init().


ipeth_main()


ipeth_main() helps initialize an ip port whose underlying link layer device is an ethernet device. ipeth_main() calls eth_ioctl(), which configures the ethernet file descriptor that corresponds to the ip port and then calls arp_set_cb() to initialize the arp port that is associated with this ip port. After the initialization, ipeth_main() calls do_eth_read() to process any ethernet packets that have arrived at the ethernet file descriptor that was just opened.


0035143          }
0035144 }
0035145 
0035146 PRIVATE int ip_cancel (fd, which_operation)
0035147 int fd;
0035148 int which_operation;
ip_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, ip_cancel() cancels the operation by clearing the appropriate flag (e.g., IFF_READ_IP) and then sending the file system a message indicating that the operation has been interrupted.

ip_cancel() is called only to cancel an operation if an ip device file (e.g., /dev/ip) has been directly opened. ip_cancel() will not be called if a higher-layer device file (e.g., /dev/udp) has been opened.


0035149 {
0035150          ip_fd_t *ip_fd;
0035151          acc_t *repl_res;
0035152          int result;
0035153 
0035154          ip_fd= &ip_fd_table[fd];
fd, ip_cancel()'s first parameter, is the index of the ip file descriptor within ip_fd_table[]. Get the pointer to this ip file descriptor.


0035155 
0035156          switch (which_operation)
0035157          {
0035158          case SR_CANCEL_IOCTL:
0035159                   assert (ip_fd->if_flags & IFF_GIPCONF_IP);
0035160                   ip_fd->if_flags &= ~IFF_GIPCONF_IP;
Since the IFF_GIPCONF_IP flag is the only ioctl flag that exists and therefore is the only flag that can be set for any ioctl operation, clear it.


0035161                   repl_res= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0035162                            (size_t)EINTR, (size_t)0, TRUE);
Send a message to the file system indicating that the operation has been interrupted.

The if_get_userdata field of the ip file descriptor was set to a reference of sr_get_userdata by ip_open().


sr_get_userdata()


sr_get_userdata() is the counterpart to sr_put_userdata() and does one of two things:

1) Copies data from a user process to a buffer (to be more specific, a chain of accessors) within the network service (this process). This can be either ioctl data (in which case, for_ioctl is TRUE) or data. For example, udp_setopt() (indirectly) calls sr_get_userdata() to get configuration data. Also, restart_write_fd() (indirectly) calls sr_get_userdata() before passing data onto the ip code.

2) Sends a REVIVE message to the file system (FS). For example, if an illegal option is selected while configuring a udp file descriptor, reply_thr_get() is called, which then (indirectly) calls sr_get_userdata(), passing in EBADMODE for the parameter count. restart_write_fd() also (indirectly) calls sr_get_userdata() to send a REVIVE message back to the FS indicating the number of bytes read after copying the data from the user process.

sr_get_userdata() is often called twice in close succession. The first time to attempt to copy the data from the user process and then the second time to send a message to the FS indicating whether the copy operation was successful and, if it was successful, the number of bytes copied.

In my opinion, like sr_put_userdata(), this function should have been made into two functions. As it is, it is too confusing.


0035163                   assert (!repl_res);
0035164                   break;
0035165          case SR_CANCEL_READ:
0035166                   assert (ip_fd->if_flags & IFF_READ_IP);
0035167                   ip_fd->if_flags &= ~IFF_READ_IP;
0035168                   result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0035169                            (size_t)EINTR, (acc_t *)0, FALSE);
Send a message to the file system indicating that the operation has been interrupted.

The if_put_userdata field of the ip file descriptor was set to a reference of sr_put_userdata by ip_open().


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.


0035170                   assert (!result);
0035171                   break;
0035172 #if 0
Apparently, writes to ip device files cannot be cancelled.


0035173          case SR_CANCEL_WRITE:
0035174                   assert(0);
0035175                   assert (ip_fd->if_flags & IFF_WRITE_MASK);
0035176                   ip_fd->if_flags &= ~IFF_WRITE_MASK;
0035177                   repl_res= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0035178                            (size_t)EINTR, (size_t)0, FALSE);
0035179                   assert (!repl_res);
0035180                   break;
0035181 #endif
0035182 #if !CRAMPED
0035183          default:
0035184                   ip_panic(( "unknown cancel request" ));
0035185 #endif
0035186          }
0035187          return NW_OK;
0035188 }
0035189 
0035190 
0035191 PUBLIC int ip_open (port, srfd, get_userdata, put_userdata, put_pkt)
0035192 int port;
0035193 int srfd;
0035194 get_userdata_t get_userdata;
0035195 put_userdata_t put_userdata;
0035196 put_pkt_t put_pkt;
ip_open()

ip_open() finds an available ip file descriptor in ip_fd_table[], sets a few of the ip file descriptor's fields, and then returns the index of the ip file descriptor within ip_fd_table[]. ip_open() is called by higher-level code (e.g., udp_main()) and the returned ip file descriptor is then associated with a higher-level port (e.g., udp port).



Note that there will only be a few ip file descriptors open at any given time. There will be an ip file descriptor opened for each interface for each client (udp, tcp, and icmp) and there will be one ip file descriptor opened each time the /dev/ip file is opened directly (as opposed to when, for example, the /dev/udp file is opened).


0035197 {
0035198          int i;
0035199          ip_fd_t *ip_fd;
0035200          ip_port_t *ip_port;
0035201 
0035202          ip_port= &ip_port_table[port];
For the following inet.config file:

eth0 DP8390 0 { default; };
psip1;

there will be 2 ports; port 0 will be for the ethernet interface and port 1 will be for the psip interface.


0035203          if (!(ip_port->ip_flags & IPF_CONFIGURED))
0035204                   return ENXIO;
ENXIO is #define'd in /include/errno.h:

#define ENXIO (_SIGN 6) /* no such device or address */



0035205 
0035206          for (i=0; i<IP_FD_NR && (ip_fd_table[i].if_flags & IFF_INUSE);
0035207                   i++);
Find the next available ip file descriptor and, if one is available, get a pointer to the ip file descriptor (see line 35215).


0035208 
0035209          if (i>=IP_FD_NR)
IP_FD_NR is #define'd in inet/generic/ip_int.h:

#define IP_FD_NR (8*IP_PORT_MAX)

IP_PORT_MAX is #define'd in inet_config.h:

#define IP_PORT_MAX 4

In other words, there is a maximum of 4 ip ports and ip_fd_table[] has 32 elements.

If i is larger than IP_FD_NR, then there are no more ip file descriptors to allocate so return EAGAIN.


0035210          {
0035211                   DBLOCK(1, printf("out of fds\n"));
0035212                   return EAGAIN;
0035213          }
0035214 
0035215          ip_fd= &ip_fd_table[i];
The remaining lines of code in ip_open() set various fields of the ip file descriptor that was acquired.


ip_fd / ip_fd_table[]


If a process opens up an ip device file (e.g., /dev/ip), an "ip file descriptor" is acquired to handle all read, write, and ioctl operations. Also, during the initialization of the higher-layer protocols (e.g., udp), an ip file descriptor is acquired to handle the read, write, and ioctl operations from the higher-layer port (e.g., udp_port). The relationships between various ports and file descriptors are shown in this figure:



ip file descriptors are ip_fd struct's. ip_fd_table[] is an array of all the ip file descriptors.

typedef struct ip_fd

{
int if_flags;
struct nwio_ipopt if_ipopt;
ip_port_t *if_port;
struct ip_fd *if_proto_next;
int if_srfd;
acc_t *if_rdbuf_head;
acc_t *if_rdbuf_tail;
get_userdata_t if_get_userdata;
put_userdata_t if_put_userdata;
put_pkt_t if_put_pkt;
time_t if_exp_time;
size_t if_rd_count;
} ip_fd_t;


typedef struct nwio_ipopt
{
u32_t nwio_flags;
ipaddr_t nwio_rem;
ip_hdropt_t nwio_hdropt;
u8_t nwio_tos;
u8_t nwio_ttl;
u8_t nwio_df;
ipproto_t nwio_proto;
} nwio_ipopt_t;


typedef struct ip_hdropt
{
u8_t iho_opt_siz;
u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE];
} ip_hdropt_t;
int if_flags:

#define IFF_EMPTY 0x0
#define IFF_INUSE 0x1
#define IFF_OPTSET 0x2
#define IFF_BUSY 0xC
#define IFF_READ_IP 0x4
#define IFF_GIPCONF_IP 0x8

During the initialization of ip_fd_table[], each ip_fd is set to IFF_EMPTY. After the ip file descriptor has been allocated, IFF_INUSE is set to indicate that the ip file descriptor is no longer available. And after the ip file descriptor has been configured, IFF_OPTSET is set. The ip file descriptor cannot be used until it has been configured. Later, when a higher layer (e.g., udp) wants to read from the ip file descriptor, the IFF_READ_IP flag is set.


struct nwio_ipopt if_ipopt: (fields shown below)


ip_port_t *if_port:

if_port points to the ip file descriptor's associated ip port. For example, if udp_port_table[0] is being initialized, ip_open() returns an ip file descriptor. if_port of this ip file descriptor will point to ip_port_table[0].


struct ip_fd *if_proto_next:

Points to the next ip file descriptor in the ip_proto[] or ip_proto_any linked lists.


int if_srfd:

if_srfd is the port number/file descriptor of the layer immediately above the ip layer. For example, if udp opens up this file descriptor, if_srfd will be the port number of the port that opened up this ip file descriptor. If, on the other hand, this file descriptor is opened up directly by the user (i.e., the user opens up /dev/ip instead of /dev/tcp or /dev/up), if_srfd will be the corresponding slot in sr_fd_table[]. Since if_srfd could be either a port or a file descriptor (fd), if_srfd is probably not the best name for this field.


acc_t *if_rdbuf_head:
acc_t *if_rdbuf_tail:


if_rdbuf_head is the head of a linked list of buffers (acc_t struct's) that are waiting to be read by a higher layer (e.g., udp). The acc_t struct's are linked together by the acc_ext_link field.


get_userdata_t if_get_userdata:
put_userdata_t if_put_userdata:
put_pkt_t if_put_pkt:


The three fields above are all pointers to functions that move packets and configuration data from one layer to the next. For example, if the udp code opens up an ip file descriptor, if_get_userdata, if_put_userdata, and if_put_pkt are set to udp_get_data(), udp_put_data(), and udp_ip_arrived().

These three functions are used to move packets and configuration data to and from the layer above (e.g., udp).


time_t if_exp_time:

Only a certain amount of time can pass from the time a packet is placed in the ip file descriptor's read queue until the packet is read. if_exp_time is this expiration time for the packet. If the packet is not read, the packet and all the later packets in the queue are deleted.


size_t if_rd_count:

The number of bytes to be read. if_rd_count is set in ip_read(). If the udp code opened up the ip file descriptor, this if_rd_count is always UDP_MAX_DATAGRAM.


u32_t nwio_flags:

nwio_flags will be a combination of the flags below. Most of the flags within a set are exclusionary. For example, both NWIO_REMSPEC and NWIO_REMANY can't both be set.

Note that "EN" stands for "ENable" and "DI" stands for "DIsable".

#define NWIO_EXCL 0x00000001l
#define NWIO_SHARED 0x00000002l
#define NWIO_COPY 0x00000003l

From ip(4):

"The options covered by NWIO_ACC_MASK control the number of channels that can use one IP protocol. If NWIO_EXCL is specified then only that channel can use a certain IP protocol. If NWIO_SHARED then multiple channels that all have to specify NWIO_SHARED can use the same IP protocol, but incoming packets will be delivered to a most one channel. NWIO_COPY does not impose any restrictions. Every channel gets a copy of an incoming packet."

Note that, for whatever reason, NWIO_EXCL behaves exactly as NWIO_COPY. Every channel receives a copy of an incoming packet.

The access flags are important during the read of an ip packet.

#define NWIO_EN_LOC 0x00000010l
#define NWIO_DI_LOC 0x00100000l
#define NWIO_EN_BROAD 0x00000020l
#define NWIO_DI_BROAD 0x00200000l

NWIO_EN_LOC specifies that this file descriptor can receive packets destined for this machine and NWIO_EN_BROAD specifies that this file descriptor can receive broadcast packets.

#define NWIO_REMSPEC 0x00000100l
#define NWIO_REMANY 0x01000000l

If the NWIO_REMANY flag is set, this file descriptor can send packets to any destination. If, on the other hand, the NWIO_REMSPEC flag is set, this file descriptor can only communicate with a single host. This host is specified by nwio_rem (see below).

#define NWIO_PROTOSPEC 0x00000200l
#define NWIO_PROTOANY 0x02000000l

If NWIO_PROTOANY is set, the ip file descriptor will accept packets of any protocol type. However, if NWIO_PROTOSPEC is set, only packets with a protocol type of nwio_proto (see below) are accepted.

#define NWIO_HDR_O_SPEC 0x00000400l
#define NWIO_HDR_O_ANY 0x04000000l

If the NWIO_HDR_O_SPEC flag in nwio_flags is set, nwio_hdropt (see below) must be set. If this is the case, the extra header information for all outgoing packets will be taken from nwio_hdropt, nwio_tos, nwio_ttl, and nwio_df (see below).

#define NWIO_RWDATONLY 0x00001000l
#define NWIO_RWDATALL 0x10000000l

NWIO_RWDATALL is a little tricky. If the NWIO_RWDATALL flag is set, the header was omitted when passing the packet down to ip code and the NWIO_EN_LOC, NWIO_DI_BROAD, NWIO_REMSPEC, NWIO_PROTOSPEC and NWIO_HDR_O_SPEC flags must all be set (and NWIO_REMANY and NWIO_PROTOANY cannot be set). In other words, this file descriptor can only send the data to one destination using one protocol.

During the configuration of an ip file descriptor being opened by the udp code, ip_ioctl() calls udp_get_data() (indirectly) to get configuration information. udp_get_data() returns the following configuration information:

NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | NWIO_HDR_O_ANY | NWIO_RWDATALL

ipaddr_t nwio_rem:

If the NWIO_REMSPEC flag in nwio_flags is set (see above), nwio_rem is the ip address of the destination host.


ip_hdropt_t nwio_hdropt:

The ip header length is flexible to allow for extra options. For example, in addition to the normal fields (e.g., destination ip address), an ip header may specify the route it wishes to take or request that the route be recorded.

that it wishes to record a route or to ip_chk_hdropt().


u8_t nwio_tos:

"tos" stands for "Type Of Service". nwio_tos is initialized to 0 but can be changed by ip_ioctl().


u8_t nwio_ttl:

"ttl" stands for "Time To Live", which is the number of hops that a packet can take before being dropped by a router. nwio_ttl is initialized to 255 but can be changed by ip_ioctl().


u8_t nwio_df:

nwio_df specifies whether fragmentation is allowed or not. nwio_df is initialized to FALSE but, again, can be changed by ip_ioctl().


ipproto_t nwio_proto:

nwio_proto can take one of the values below. Obviously, if the udp code opens up an ip file descriptor, nwio_proto will be IPPROTO_UDP. The same is true for icmp and tcp.

#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17

This field is used in conjunction with the NWIO_PROTOSPEC flag in nwio_flags. If this flag is set, nwio_proto must be set.


ip_hdropt: If the NWIO_HDR_O_SPEC flag is set (see above), any outgoing packets will have ip options as specified by iho_data[]. The length of the options will then be iho_opt_siz.

u8_t iho_opt_siz: The length of the options.

u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE]: The actual options.


0035216 
0035217          ip_fd->if_flags= IFF_INUSE;
Mark the ip file descriptor as no longer available.


0035218 
0035219          ip_fd->if_ipopt.nwio_flags= NWIO_DEFAULT;
NWIO_DEFAULT is #define'd in ip_int.h:

#define NWIO_DEFAULT (NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | \
NWIO_RWDATALL | NWIO_HDR_O_SPEC)

A description of these flags as well and the fields below is given in the ip file descriptor general comment.

Note that the nwio_flags is typically reset when opened by an upper-layer protocol. For example, when the udp code opens an ip file descriptor, it resets the ip file descriptor's nwio_flags and nwio_proto fields.



0035220          ip_fd->if_ipopt.nwio_tos= 0;
0035221          ip_fd->if_ipopt.nwio_df= FALSE;
0035222          ip_fd->if_ipopt.nwio_ttl= 255;
0035223          ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz= 0;
0035224 
0035225          ip_fd->if_port= ip_port;
Each ip file descriptor has an associated ip port. The if_port field of the ip file descriptor is the ip file descriptor's associated ip port.


0035226          ip_fd->if_srfd= srfd;
0035227          assert(ip_fd->if_rdbuf_head == NULL);
0035228          ip_fd->if_get_userdata= get_userdata;
0035229          ip_fd->if_put_userdata= put_userdata;
0035230          ip_fd->if_put_pkt= put_pkt;
0035231          return i;
Return the index within ip_fd_table[] of the newly acquired ip file descriptor.


0035232 }
0035233 
0035234 PRIVATE void ip_close (fd)
0035235 int fd;
ip_close()

ip_close() simply closes a previously opened ip file descriptor. To be more specific, ip_close() removes the ip file descriptor from its protocol-specific linked list, frees all the packets in its read queue, and marks the ip file descriptor as available.

ip_close() is called only to close an ip device file (e.g., /dev/ip) that has been directly opened. ip_close() will not be called if a higher-layer device file (e.g., /dev/udp) has been opened.


0035236 {
0035237          ip_fd_t *ip_fd;
0035238          acc_t *pack;
0035239 
0035240          ip_fd= &ip_fd_table[fd];
0035241 
0035242          assert ((ip_fd->if_flags & IFF_INUSE) &&
0035243                   !(ip_fd->if_flags & IFF_BUSY));
0035244 
0035245          if (ip_fd->if_flags & IFF_OPTSET)
0035246                   ip_unhash_proto(ip_fd);
If the ip file descriptor has been configured (i.e., IFF_OPTSET has been set), the ip file descriptor has been placed in one of the queues.


ip_hash_proto() / ip_unhash_proto()


An ip file descriptor either accepts packets of any protocol type (the NWIO_PROTOANY flag for the file descriptor is set) or accepts packets of a specific protocol (NWIO_PROTOSPEC is set). A few of the different protocols that a packet may have are ICMP, UDP, TCP, EGP, and OSPF.

When a packet arrives at an ip port, the ip port must find the appropriate ip file descriptors and then deliver the packets to them.

In order to find matching ip file descriptors quickly, ip ports have one linked list (ip_proto_any - for ip file descriptors with the NWIO_PROTOANY flag set) and one array of linked lists (ip_proto[] - NWIO_PROTOSPEC is set). When a packet arrives at an ip port, the ip port first searches for matching ip file descriptors in the ip_proto_any linked list and then in the ip_port[] element that corresponds to the protocol of the packet (actually, it's a hash - several protocols will share a single element). In this way, the ip port avoids searching through all of the ip file descriptors.

ip_hash_proto(ip_fd) puts an ip file descriptor (ip_fd) into the appropriate linked list.

This ip file descriptor may be later removed from the linked list by ip_unhash_proto(ip_fd).


0035247          while (ip_fd->if_rdbuf_head)
Free the packets in the ip file descriptor's read queue.


0035248          {
0035249                   pack= ip_fd->if_rdbuf_head;
0035250                   ip_fd->if_rdbuf_head= pack->acc_ext_link;
0035251                   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).


0035252          }
0035253          ip_fd->if_flags= IFF_EMPTY;
Mark the ip file descriptor as available.


0035254 }
0035255 
0035256 PRIVATE void ip_buffree(priority)
0035257 int priority;
ip_buffree()

ip_buffree(priority) is called by bf_memreq() if bf_memreq() does not have enough accessors to satisfy a buffer request.

priority, ip_buffree()'s only parameter, will be either IP_PRI_PORTBUFS (#define'd as 3), IP_PRI_ASSBUFS (4), IP_PRI_FDBUFS_EXTRA (5), or IP_PRI_FDBUFS (6). If priority is IP_PRI_PORTBUFS, all packets (which are held in accessors) in the ip port's loopback queue (i.e., packets in the ip_loopb_head queue) are freed. In addition, accessors specific to an ip port's underlying data link layer are freed. For example, if the underlying data link layer protocol is ethernet, packets waiting for arp resolution (i.e., packets in the de_arp_head queue) and packets waiting to be sent out (i.e., packets in the de_q_head queue) are freed. If priority is IP_PRI_FDBUFS, all packets in the read queues of all ip file descriptors are freed. If priority is IP_PRI_FDBUFS_EXTRA, every packet except the last packet in the read queue of each ip file descriptor is freed. If priority is IP_PRI_ASBUFS, all fragments in ip_ass_table[] are freed.


0035258 {
0035259          int i;
0035260          ip_port_t *ip_port;
0035261          ip_fd_t *ip_fd;
0035262          ip_ass_t *ip_ass;
0035263          acc_t *pack, *next_pack;
0035264 
0035265          for (i= 0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++)
0035266          {
0035267                   if (ip_port->ip_dl_type == IPDL_ETH)
If priority is IP_PRI_PORTBUFS, in addition to freeing packets held in the loopback queue (see lines 37307 - 37332), accessors specific to an ip port's underlying data link layer are freed. If the underlying data link layer protocol is ethernet, packets waiting for arp resolution (i.e., packets in the de_arp_head queue) and packets waiting to be sent out (i.e., packets in the de_q_head queue) are freed.


0035268                   {
0035269                            /* Can't free de_frame.
0035270                             * bf_check_acc(ip_port->ip_dl.dl_eth.de_frame);
0035271                             */
0035272                            if (priority == IP_PRI_PORTBUFS)
0035273                            {
0035274                                     next_pack= ip_port->ip_dl.dl_eth.de_arp_head;
0035275                                     while(next_pack != NULL)
0035276                                     {
0035277                                              pack= next_pack;
0035278                                              next_pack= pack->acc_ext_link;
0035279                                              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).


0035280                                     }
0035281                                     ip_port->ip_dl.dl_eth.de_arp_head= next_pack;
0035282 
0035283                                     next_pack= ip_port->ip_dl.dl_eth.de_q_head;
0035284                                     while(next_pack != NULL)
0035285                                     {
0035286                                              pack= next_pack;
0035287                                              next_pack= pack->acc_ext_link;
0035288                                              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).


0035289                                     }
0035290                                     ip_port->ip_dl.dl_eth.de_q_head= next_pack;
0035291                            }
0035292                   }
0035293                   else if (ip_port->ip_dl_type == IPDL_PSIP)
The psip data link layer is not covered in this documentation.


0035294                   {
0035295                            if (priority == IP_PRI_PORTBUFS)
0035296                            {
0035297                                     next_pack= ip_port->ip_dl.dl_ps.ps_send_head;
0035298                                     while(next_pack != NULL)
0035299                                     {
0035300                                              pack= next_pack;
0035301                                              next_pack= pack->acc_ext_link;
0035302                                              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).


0035303                                     }
0035304                                     ip_port->ip_dl.dl_ps.ps_send_head= next_pack;
0035305                            }
0035306                   }
0035307                   if (priority == IP_PRI_PORTBUFS)
If priority is IP_PRI_PORTBUFS, all packets (which are held in accessors) in the ip port's loopback queue (i.e., packets in the ip_loopb_head queue) are freed.


0035308                   {
0035309                            next_pack= ip_port->ip_loopb_head;
0035310                            while(next_pack && next_pack->acc_ext_link)
0035311                            {
0035312                                     pack= next_pack;
0035313                                     next_pack= pack->acc_ext_link;
0035314                                     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).


0035315                            }
0035316                            if (next_pack)
0035317                            {
0035318                                     if (ev_in_queue(&ip_port->ip_loopb_event))
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.


0035319                                     {
0035320 #if !CRAMPED
0035321                                              printf(
0035322 "not freeing ip_loopb_head, ip_loopb_event enqueued\n");
0035323 #endif
0035324                                     }
0035325                                     else
0035326                                     {
0035327                                              bf_afree(next_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).


0035328                                              next_pack= NULL;
0035329                                     }
0035330                            }
0035331                            ip_port->ip_loopb_head= next_pack;
0035332                   }
0035333          }
0035334          if (priority == IP_PRI_FDBUFS_EXTRA)
If priority is IP_PRI_FDBUFS_EXTRA, every packet except the last packet in the read queue of each ip file descriptors is freed.


0035335          {
0035336                   for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
0035337                   {
0035338                            while (ip_fd->if_rdbuf_head &&
0035339                                     ip_fd->if_rdbuf_head->acc_ext_link)
0035340                            {
0035341                                     pack= ip_fd->if_rdbuf_head;
0035342                                     ip_fd->if_rdbuf_head= pack->acc_ext_link;
0035343                                     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).


0035344                            }
0035345                   }
0035346          }
0035347          if (priority == IP_PRI_FDBUFS)
If priority is IP_PRI_FDBUFS, all packets in the read queues of all ip file descriptors are freed.


0035348          {
0035349                   for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
0035350                   {
0035351                            while (ip_fd->if_rdbuf_head)
0035352                            {
0035353                                     pack= ip_fd->if_rdbuf_head;
0035354                                     ip_fd->if_rdbuf_head= pack->acc_ext_link;
0035355                                     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).


0035356                            }
0035357                   }
0035358          }
0035359          if (priority == IP_PRI_ASSBUFS)
If priority is IP_PRI_ASBUFS, all fragments in ip_ass_table[] are freed.


0035360          {
0035361                   for (i= 0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++)
0035362                   {
0035363                            next_pack= ip_ass->ia_frags;
0035364                            while(ip_ass->ia_frags != NULL)
0035365                            {
0035366                                     pack= ip_ass->ia_frags;
0035367                                     ip_ass->ia_frags= pack->acc_ext_link;
0035368                                     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).


0035369                            }
0035370                            ip_ass->ia_first_time= 0;
0035371                   }
0035372          }
0035373 }
0035374 

/*
The following is not used in the default configuration.





0035375 #ifdef BUF_CONSISTENCY_CHECK
0035376 PRIVATE void ip_bufcheck()
0035377 {
0035378          int i;
0035379          ip_port_t *ip_port;
0035380          ip_fd_t *ip_fd;
0035381          ip_ass_t *ip_ass;
0035382          acc_t *pack;
0035383 
0035384          for (i= 0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++)
0035385          {
0035386                   if (ip_port->ip_dl_type == IPDL_ETH)
0035387                   {
0035388                            bf_check_acc(ip_port->ip_dl.dl_eth.de_frame);
0035389                            for (pack= ip_port->ip_dl.dl_eth.de_q_head; pack;
0035390                                     pack= pack->acc_ext_link)
0035391                            {
0035392                                     bf_check_acc(pack);
0035393                            }
0035394                            for (pack= ip_port->ip_dl.dl_eth.de_arp_head; pack;
0035395                                     pack= pack->acc_ext_link)
0035396                            {
0035397                                     bf_check_acc(pack);
0035398                            }
0035399                   }
0035400                   else if (ip_port->ip_dl_type == IPDL_PSIP)
0035401                   {
0035402                            for (pack= ip_port->ip_dl.dl_ps.ps_send_head; pack;
0035403                                     pack= pack->acc_ext_link)
0035404                            {
0035405                                     bf_check_acc(pack);
0035406                            }
0035407                   }
0035408                   for (pack= ip_port->ip_loopb_head; pack;
0035409                            pack= pack->acc_ext_link)
0035410                   {
0035411                            bf_check_acc(pack);
0035412                   }
0035413          }
0035414          for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
0035415          {
0035416                   for (pack= ip_fd->if_rdbuf_head; pack;
0035417                            pack= pack->acc_ext_link)
0035418                   {
0035419                            bf_check_acc(pack);
0035420                   }
0035421          }
0035422          for (i= 0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++)
0035423          {
0035424                   for (pack= ip_ass->ia_frags; pack; pack= pack->acc_ext_link)
0035425                            bf_check_acc(pack);
0035426          }
0035427 }
0035428 #endif /* BUF_CONSISTENCY_CHECK */
0035429 

/*





0035430 PRIVATE void ip_bad_callback(ip_port)
0035431 struct ip_port *ip_port;
0035432 {
0035433 #if !CRAMPED
0035434          ip_panic(( "no callback filled in for port %d", ip_port->ip_port ));
0035435 #endif
0035436 }
0035437 
0035438 /*
0035439  * $PchId: ip.c,v 1.7 1996/12/17 07:54:47 philip Exp $
0035440  */