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();
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 {
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:
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:
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
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;
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;
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;
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)
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;
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)
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)
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)
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
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 */