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

0039001 /*
0039002 ip_ioctl.c
0039003 
0039004 Copyright 1995 Philip Homburg
0039005 */
ip_ioctl.c, as the name suggests, contains the code that handles ioctl requests for ip file descriptors and ip ports. These requests include:

NWIOSIPOPT: Set the options (the if_ipopt field of the ip file descriptor) on the ip file descriptor.

NWIOGIPOPT: Send the ip file descriptor's options to the user process requesting the information.

NWIOSIPCONF: Configure the ip port (for example, the ip address can be configured) that corresponds to the ip file descriptor fd.

NWIOGIPCONF: Send the ip address/subnet information (i.e., send a nwio_ipconf_t struct) to the next higher layer.

NWIOGIPIROUTE, NWIOSIPIROUTE, NWIOGIPOROUTE, NWIODIPIROUTE, NWIOSIPOROUTE: It is possible to influence the route taken by a packet. These ioctl requests alter the input and output routing tables.


0039006 
0039007 #include "inet.h"
0039008 #include "buf.h"
0039009 #include "event.h"
0039010 #include "type.h"
0039011 
0039012 #include "arp.h"
0039013 #include "assert.h"
0039014 #include "clock.h"
0039015 #include "icmp_lib.h"
0039016 #include "ip.h"
0039017 #include "ip_int.h"
0039018 #include "ipr.h"
0039019 
0039020 THIS_FILE
0039021 
0039022 FORWARD int ip_checkopt ARGS(( ip_fd_t *ip_fd ));
0039023 FORWARD void reply_thr_get ARGS(( ip_fd_t *ip_fd, size_t
0039024          reply, int for_ioctl ));
0039025 
0039026 PUBLIC int ip_ioctl (fd, req)
0039027 int fd;
0039028 ioreq_t req;
ip_ioctl()

ip_ioctl(fd, req) performs one of several tasks on the ip file descriptor whose index within ip_fd_table[] is fd, the first parameter. The task performed depends on req, the second parameter.

NWIOSIPOPT: Set the options (the if_ipopt field of the ip file descriptor) on the ip file descriptor. For example, during the initialization of a physical udp port, ip_ioctl() is called with req equal to NWIOSIPOPT.

An example of an ip iption (i.e., ip flag) is the NWIO_EN_BROAD flag. This flag is set if the ip file descriptor accepts broadcast packets. The options desired are obtained from the user process. For example, if a udp port opened up the ip file descriptor, udp_get_data() is (indirectly) called to obtain the configuration data.

NWIOGIPOPT: Send the ip file descriptor's options to the user process requesting the information. The information is sent in a struct of type nwio_ipopt_t.

NWIOSIPCONF: Configure the ip port (for example, the ip address can be configured) that corresponds to the ip file descriptor fd. The fields are obtained from the user process. For a detailed description of the different settings, click here.

NWIOGIPCONF: Send the ip address/subnet information (i.e., send a nwio_ipconf_t struct) to the next higher layer. For example, if the next higher layer is udp, ip_ioctl() calls (indirectly) udp_put_data(), which sets the ip address for the udp port (i.e., sets the up_ipaddr field of the corresponding element in udp_port_table[]).

NWIOGIPIROUTE, NWIOSIPIROUTE, NWIOGIPOROUTE, NWIODIPIROUTE, NWIOSIPOROUTE: It is possible to influence the route taken by a packet. These ioctl requests alter the input and output routing tables.


0039029 {
0039030          ip_fd_t *ip_fd;
Since one of the functions of ip_ioctl() is to get and set the options for an ip file descriptor, it is important to understand the different fields of an ip file descriptor.


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.


0039031          ip_port_t *ip_port;
0039032          nwio_ipopt_t *ipopt;
0039033          nwio_ipopt_t oldopt, newopt;
0039034          nwio_ipconf_t *ipconf;
0039035          nwio_route_t *route_ent;
0039036          acc_t *data;
0039037          int result;
0039038          unsigned int new_en_flags, new_di_flags,
0039039                   old_en_flags, old_di_flags;
0039040          unsigned long new_flags;
0039041          int old_ip_flags;
0039042          int ent_no;
0039043 
0039044          assert (fd>=0 && fd<=IP_FD_NR);
0039045          ip_fd= &ip_fd_table[fd];
Find the correct ip file descriptor whose index within ip_fd_table[] is fd (the first parameter to ip_ioctl()).


0039046 
0039047          assert (ip_fd->if_flags & IFF_INUSE);
0039048 
0039049          switch (req)
0039050          {
0039051          case NWIOSIPOPT:
Set the options for the ip file descriptor.

From ip(4):

"Before an IP channel can be used, it has to be configured using the NWIOSIPOPT ioctl.".

For example, before a udp port may be used, ip_ioctl() is called with its second argument set to NWIOSIPOPT.

An example of an option (i.e., ip flag) is the NWIO_EN_BROAD. This flag enables the ip file descriptor to receive broadcast packets.

After getting the option information from the user process (lines 39052-39053), ip_ioctl() determines whether the requested options are acceptable (lines 39056-39141) before committing them to the ip file descriptor (lines 39146-39147).


nwio_ipopt


The nwio_ipopt struct is used to pass ip option values from a higher-layer level (e.g., icmp, udp) to the ip layer during the configuration of an ip file descriptor. Note that an ip file descriptor cannot be used until it is configured.

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;
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 file descriptor.

#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.






0039052                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, 0,
0039053                            sizeof(nwio_ipopt_t), TRUE);
The if_get_userdata field of an ip file descriptor is set by ip_open() during initialization of the file descriptor. For an ip file descriptor opened by the udp code, if_get_userdata references udp_get_data().

If the udp code does open the ip file descriptor, udp_get_data() returns an nwio_ipopt_t struct with the following settings:

nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | NWIO_HDR_O_ANY | NWIO_RWDATALL;

nwio_proto= IPPROTO_UDP;

If the icmp code opens the ip file descriptor, the if_get_userdata field is set to icmp_getdata().


0039054 
0039055                   data= bf_packIffLess (data, sizeof(nwio_ipopt_t));
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039056                   assert (data->acc_length == sizeof(nwio_ipopt_t));
0039057 
0039058                   ipopt= (nwio_ipopt_t *)ptr2acc_data(data);
0039059                   oldopt= ip_fd->if_ipopt;
0039060                   newopt= *ipopt;
0039061 
Bits 0-15 are the enable bits and bits 16-31 are the disable bits for the option's flags. For example, the broadcast enable flag (NWIO_EN_BROAD) is 0x00000020 and the broadcast disable flag (NWIO_DI_BROAD) is 0x00200000.


0039062                   old_en_flags= oldopt.nwio_flags & 0xffff;
0039063                   old_di_flags= (oldopt.nwio_flags >> 16) & 0xffff;
0039064                   new_en_flags= newopt.nwio_flags & 0xffff;
0039065                   new_di_flags= (newopt.nwio_flags >> 16) & 0xffff;
0039066                   if (new_en_flags & new_di_flags)
You can't simultaneously enable and disable a flag.


0039067                   {
0039068                            bf_afree(data);
0039069                            reply_thr_get (ip_fd, EBADMODE, TRUE);
0039070                            return NW_OK;
0039071                   }
0039072 
0039073                   /* NWIO_ACC_MASK */
0039074                   if (new_di_flags & NWIO_ACC_MASK)
0039075                   {
0039076                            bf_afree(data);
0039077                            reply_thr_get (ip_fd, EBADMODE, TRUE);
0039078                            return NW_OK;
0039079                            /* access modes can't be disable */
0039080                   }
0039081 
For lines 39082-39131, if no flag within a set of flags (e.g., NWIO_ACC_MASK) is either disabled or enabled, use the old flags and their associated fields For the NWIO_REM_MASK set of flags, use the old value of nwio_rem.

For a detailed description of the flags below, click here.



0039082                   if (!(new_en_flags & NWIO_ACC_MASK))
0039083                            new_en_flags |= (old_en_flags & NWIO_ACC_MASK);
0039084 
0039085                   /* NWIO_LOC_MASK */
0039086                   if (!((new_en_flags|new_di_flags) & NWIO_LOC_MASK))
0039087                   {
0039088                            new_en_flags |= (old_en_flags & NWIO_LOC_MASK);
0039089                            new_di_flags |= (old_di_flags & NWIO_LOC_MASK);
0039090                   }
0039091 
0039092                   /* NWIO_BROAD_MASK */
0039093                   if (!((new_en_flags|new_di_flags) & NWIO_BROAD_MASK))
0039094                   {
0039095                            new_en_flags |= (old_en_flags & NWIO_BROAD_MASK);
0039096                            new_di_flags |= (old_di_flags & NWIO_BROAD_MASK);
0039097                   }
0039098 
0039099                   /* NWIO_REM_MASK */
0039100                   if (!((new_en_flags|new_di_flags) & NWIO_REM_MASK))
0039101                   {
0039102                            new_en_flags |= (old_en_flags & NWIO_REM_MASK);
0039103                            new_di_flags |= (old_di_flags & NWIO_REM_MASK);
0039104                            newopt.nwio_rem= oldopt.nwio_rem;
0039105                   }
0039106 
0039107                   /* NWIO_PROTO_MASK */
0039108                   if (!((new_en_flags|new_di_flags) & NWIO_PROTO_MASK))
0039109                   {
0039110                            new_en_flags |= (old_en_flags & NWIO_PROTO_MASK);
0039111                            new_di_flags |= (old_di_flags & NWIO_PROTO_MASK);
0039112                            newopt.nwio_proto= oldopt.nwio_proto;
0039113                   }
0039114 
0039115                   /* NWIO_HDR_O_MASK */
0039116                   if (!((new_en_flags|new_di_flags) & NWIO_HDR_O_MASK))
0039117                   {
0039118                            new_en_flags |= (old_en_flags & NWIO_HDR_O_MASK);
0039119                            new_di_flags |= (old_di_flags & NWIO_HDR_O_MASK);
0039120                            newopt.nwio_tos= oldopt.nwio_tos;
0039121                            newopt.nwio_ttl= oldopt.nwio_ttl;
0039122                            newopt.nwio_df= oldopt.nwio_df;
0039123                            newopt.nwio_hdropt= oldopt.nwio_hdropt;
0039124                   }
0039125 
0039126                   /* NWIO_RW_MASK */
0039127                   if (!((new_en_flags|new_di_flags) & NWIO_RW_MASK))
0039128                   {
0039129                            new_en_flags |= (old_en_flags & NWIO_RW_MASK);
0039130                            new_di_flags |= (old_di_flags & NWIO_RW_MASK);
0039131                   }
0039132 
0039133                   new_flags= ((unsigned long)new_di_flags << 16) | new_en_flags;
0039134 
0039135                   if ((new_flags & NWIO_RWDATONLY) && (new_flags &
0039136                            (NWIO_REMANY|NWIO_PROTOANY|NWIO_HDR_O_ANY)))
If an ip file descriptor's NWIO_RWDATONLY flag is set, the ip file descriptor must specify the remote host to which to send packets and from which to receive packets and must specify the protocol of the packets. The ip file descriptor must also specify the ip options of outgoing packets. In other words, the NWIO_REMANY,NWIO_PROTOANY, and NWIO_HDR_O_ANY flags cannot be set.


0039137                   {
0039138                            bf_afree(data);
0039139                            reply_thr_get(ip_fd, EBADMODE, TRUE);
0039140                            return NW_OK;
0039141                   }
0039142 
0039143                   if (ip_fd->if_flags & IFF_OPTSET)
0039144                            ip_unhash_proto(ip_fd);
If the ip file descriptor had been previously configured, remove it from its linked list. After all, it may now accept packets with a different protocol. The ip file descriptor will be added later (in ip_checkopt()) to the linked list appropriate for its new protocol.


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).


0039145 
0039146                   newopt.nwio_flags= new_flags;
0039147                   ip_fd->if_ipopt= newopt;
0039148 
0039149                   result= ip_checkopt(ip_fd);
ip_checkopt()

ip_checkopt() checks the validity of an ip file descriptor's options. For example, ip_checkopt() verifies that one flag (disable or enable) is set in each set of flags (e.g., the NWIO_ACC_MASK set of flags). ip_checkopt() also verifies that the ip header options are valid.

If the flags are acceptable, the ip file descriptor is placed in its corresponding ip port's appropriate linked list.


0039150 
0039151                   if (result<0)
0039152                            ip_fd->if_ipopt= oldopt;
If the new ip options are unacceptable, revert back to the old options.


0039153 
0039154                   bf_afree(data);
0039155                   reply_thr_get (ip_fd, result, TRUE);
0039156                   return NW_OK;
0039157 
0039158          case NWIOGIPOPT:
In the previous case (lines 39051-39156), the ip options for an ip file descriptor were set. Here, the ip options previously set are returned to the process (lines 39165-39166) and the success of this operation is then reported to the file system (line 39167).

Note that the udp code never calls ip_ioctl() making an NWIOGIPOPT request. (The udp code does, however, call ip_ioctl() making an NWIOSIPOT request.) The ioctl operation must be made for a ip file descriptor (and not, for example, on a udp file descriptor).


0039159                   data= bf_memreq(sizeof(nwio_ipopt_t));
nwio_ipopt

The nwio_ipopt struct is used to pass ip option values from a higher-layer level (e.g., icmp, udp) to the ip layer during the configuration of an ip file descriptor. Note that an ip file descriptor cannot be used until it is configured.

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;
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 file descriptor.

#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.






0039160 
0039161                   ipopt= (nwio_ipopt_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039162 
0039163                   *ipopt= ip_fd->if_ipopt;
0039164 
0039165                   result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, data,
0039166                                                                    TRUE);
As mentioned above, the ip file descriptor was opened directly. Therefore, if_put_userdata will be set to a reference of sr_put_userdata(). Give the process the data.


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.


0039167                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result,
0039168                                                      (acc_t *)0, TRUE);
Report to the process whether the operation was successful.


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.


0039169 
0039170          case NWIOSIPCONF:
From ip(4):

"The NWIOSIPCONF ioctl can be used to inform the IP server about its Internet Address and/or its netmask. Normally an IP server will discover its Internet Address using the RARP protocol. NWIOSIPCONF can be used in the case that the RARP failed, or the netmask has to be changed. Note that higher level protocols (TCP and UDP) assume that the Internet Address of an IP device does not change, therefore TCP and UDP stop functioning if the Internet Address is changed."

If an NWIOSIPCONF ioctl request is made, ip_ioctl() gets data (an nwio_ipconf_t struct) from the process and then sets the ip address and/or subnet mask of the ip file descriptor's associated ip port. Finally, ip_ioctl() informs the process whether the operation was successful.

The ifconfig command can be used to make the NWIOSIPCONF request. The following is the man page for ifconfig(8):

"Ifconfig initializes a TCP/IP device setting the IP address and/or netmask. It will report the address and netmask set. This command may be used if the system has not been configured properly yet. It is only used at boot time to set a fixed address for a system without a physical ethernet. Normally the inet task will find it out by itself from the RARP server."

For this case, ifconfig, is called with the -h argument:

ifconfig [-I ip-device] [-h ipaddr] [-n netmask] [-iv]

This command actually makes an ioctl() call:

result= ioctl(ip_fd, NWIOSIPCONF, &ipconf);

ioctl() is implemented in sys/src/lib/posix/_ioctl.c and has the following prototype:

PUBLIC int ioctl(fd, request, data)

ioctl() is actually a call to:

_syscall(FS, IOCTL, &m)

where the message m has its fields set to the following:

m.TTY_LINE = fd;
m.TTY_REQUEST = request;
m.ADDRESS = (char *) data;


0039171                   ip_port= ip_fd->if_port;
0039172 
0039173                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, 0,
0039174                                               sizeof(nwio_ipconf_t), TRUE);
As with the NWIOGIPOPT request above, NWIOSIPCONF is never called by upper-layer code. Therefore, if an NWIOSIPCONF request is made, it is made on an ip file descriptor directly and so if_get_userdata will be set to sr_get_userdata().


nwio_ipconf_t


Type nwio_ipconf_t contains the ip address information of an ip port (i.e., an element in ip_port_table[]:

typedef struct nwio_ipconf

{
u32_t nwic_flags;
ipaddr_t nwic_ipaddr;
ipaddr_t nwic_netmask;
} nwio_ipconf_t;
nwic_flags: reflects whether the ip address and the subnet mask have been set. The different flags are as follows:

#define NWIC_NOFLAGS 0x0
#define NWIC_FLAGS 0x3
#define NWIC_IPADDR_SET 0x1
#define NWIC_NETMASK_SET 0x2

nwic_ipaddr: the ip address (e.g., 192.168.5.5)

nwic_netmask: the subnet mask (e.g., 255.255.255.0)


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.


0039175 
0039176                   data= bf_packIffLess (data, sizeof(nwio_ipconf_t));
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039177                   assert (data->acc_length == sizeof(nwio_ipconf_t));
0039178 
0039179                   old_ip_flags= ip_port->ip_flags;
The old_ip_flags variable is never used.


0039180 
0039181                   ipconf= (nwio_ipconf_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039182 
0039183                   if (ipconf->nwic_flags & ~NWIC_FLAGS)
If none of these flags is set, there's no point to the NWIOSIPCONF request.


0039184                   {
0039185                            bf_afree(data);
0039186                            return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd,
0039187                                                 EBADMODE, (acc_t *)0, TRUE);
If an ip device file (e.g., /dev/ip) was opened directly, the if_put_userdata of the ip file descriptor will be sr_put_userdata(). If this is the case, sr_put_userdata() complaints to the process that opened the ip file descriptor.


0039188                   }
0039189 
0039190                   if (ipconf->nwic_flags & NWIC_IPADDR_SET)
Set the ip address and possibly the subnet mask of the ip port.


0039191                   {
0039192                            ip_port->ip_ipaddr= ipconf->nwic_ipaddr;
0039193                            ip_port->ip_flags |= IPF_IPADDRSET;
0039194                            ip_port->ip_netmask=
0039195                                     ip_netmask(ip_nettype(ipconf->nwic_ipaddr));
ip_nettype()

ip_nettype(ipaddr) returns the network type (which will be of type nettype_t) of ipaddr, the only parameter to ip_nettype().

The nettype_t enum typedef is declared in inet/generic/ip_int.h. Each type's associated ip address range is included in the comments.

typedef enum nettype
{
IPNT_ZERO, /* 0.xx.xx.xx */
IPNT_CLASS_A, /* 1.xx.xx.xx .. 126.xx.xx.xx */
IPNT_LOCAL, /* 127.xx.xx.xx */
IPNT_CLASS_B, /* 128.xx.xx.xx .. 191.xx.xx.xx */
IPNT_CLASS_C, /* 192.xx.xx.xx .. 223.xx.xx.xx */
IPNT_CLASS_D, /* 224.xx.xx.xx .. 239.xx.xx.xx */
IPNT_CLASS_E, /* 240.xx.xx.xx .. 247.xx.xx.xx */
IPNT_MARTIAN, /* 248.xx.xx.xx .. 254.xx.xx.xx + */
IPNT_BROADCAST /* 255.255.255.255 */
} nettype_t;



0039196                            if (!(ip_port->ip_flags & IPF_NETMASKSET)) {
0039197                                     ip_port->ip_subnetmask= ip_port->ip_netmask;
0039198                            }
0039199                            (*ip_port->ip_dev_set_ipaddr)(ip_port);
If the underlying data link layer is ethernet, ip_dev_set_ipaddr was set to the reference of ipeth_set_ipaddr().


ipeth_set_ipaddr()


ipeth_set_ipaddr() calls arp_set_ipaddr() to set the ap_ipaddr field of the ip port's associated arp port. If the ip port has not finished initializing, ipeth_main() is called to finish this initialization.


0039200                   }
0039201                   if (ipconf->nwic_flags & NWIC_NETMASK_SET)
Set the subnet mask of the ip port.


0039202                   {
0039203                            ip_port->ip_subnetmask= ipconf->nwic_netmask;
0039204                            ip_port->ip_flags |= IPF_NETMASKSET;
0039205                   }
0039206 
0039207                   bf_afree(data);
0039208                   return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, NW_OK,
0039209                                                      (acc_t *)0, TRUE);
If an ip device file (e.g., /dev/ip) was opened directly, the if_put_userdata of the ip file descriptor will be sr_put_userdata(). If this is the case, sr_put_userdata() reports to the process that the operation was successful.


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.


0039210 
0039211          case NWIOGIPCONF:
Unlike the NWIOSIPCONF ioctl request, the upper layers (e.g., udp) make NWIOGIPCONF (NetWork IO Get IP CONFiguration) requests. For example, the udp code makes an NWIOGIPCONF request in order to set the ip address of a udp port associated with the ip port.


0039212                   ip_port= ip_fd->if_port;
Get the ip file descriptor's associated ip port.


0039213 
0039214                   if (!(ip_port->ip_flags & IPF_IPADDRSET))
If the ip address hasn't been set, alert the caller. If the "ifconfig -h host-IP-address" command was issued to set the ip address of the port, the IPF_IPADDRSET flag was set on line 39193.


0039215                   {
0039216                            ip_fd->if_flags |= IFF_GIPCONF_IP;
0039217                            return NW_SUSPEND;
0039218                   }
0039219                   ip_fd->if_flags &= ~IFF_GIPCONF_IP;
0039220                   data= bf_memreq(sizeof(nwio_ipconf_t));
bf_memreq()

After the buffers have been initialized, accessors[] looks like the following:



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



Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.

So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.

For a complete description of the network service's buffer management, click here.nwio_ipconf_t

Type nwio_ipconf_t contains the ip address information of an ip port (i.e., an element in ip_port_table[]:

typedef struct nwio_ipconf

{
u32_t nwic_flags;
ipaddr_t nwic_ipaddr;
ipaddr_t nwic_netmask;
} nwio_ipconf_t;
nwic_flags: reflects whether the ip address and the subnet mask have been set. The different flags are as follows:

#define NWIC_NOFLAGS 0x0
#define NWIC_FLAGS 0x3
#define NWIC_IPADDR_SET 0x1
#define NWIC_NETMASK_SET 0x2

nwic_ipaddr: the ip address (e.g., 192.168.5.5)

nwic_netmask: the subnet mask (e.g., 255.255.255.0)


0039221                   ipconf= (nwio_ipconf_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039222                   ipconf->nwic_flags= NWIC_IPADDR_SET;
0039223                   ipconf->nwic_ipaddr= ip_port->ip_ipaddr;
0039224                   ipconf->nwic_netmask= ip_port->ip_subnetmask;
0039225                   if (ip_port->ip_flags & IPF_NETMASKSET)
0039226                            ipconf->nwic_flags |= NWIC_NETMASK_SET;
0039227 
If ip_ioctl() is called from the udp code with req (the second parameter of ip_ioctl()) set to NWIOGIPCONF, udp_put_data() is called twice. The first time udp_put_data() is called, the ip address of the udp port is set. The second time udp_put_data() is called, the state of the port is set to UPS_MAIN.


0039228                   result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, data,
0039229                                                                    TRUE);
If the ip file descriptor was opened by the udp code, the if_put_userdata field will be set to the reference of udp_put_data(). If this is the case, udp_put_data() will set the ip address of a udp port associated with the ip file descriptor.


udp_put_data()


During the initialization of the udp layer, the udp code calls ip_ioctl() twice. The second time, the udp code calls ip_ioctl() with an argument of NWIOGIPCONF. ip_ioctl() then (indirectly) calls udp_put_data() twice. The first call to udp_put_data() is to get the underlying ip file descriptor's ip address (to which it sets the udp port's up_ipaddr field). The second call to udp_put_data() simply sets the udp port's state to UPS_MAIN.

After the initialization (when the state of the udp port is UPS_MAIN), udp_put_data() is called only by ip_read() to handle the (unlikely) case that the underlying ip file descriptor was not already configured.


0039230                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result,
0039231                                                      (acc_t *)0, TRUE);
If the ip file descriptor was opened by the udp code, the if_put_userdata field will be set to the reference of udp_put_data(). If this is the case, udp_put_data() will simply set the state of the udp port to UPS_MAIN.


udp_put_data()


During the initialization of the udp layer, the udp code calls ip_ioctl() twice. The second time, the udp code calls ip_ioctl() with an argument of NWIOGIPCONF. ip_ioctl() then (indirectly) calls udp_put_data() twice. The first call to udp_put_data() is to get the underlying ip file descriptor's ip address (to which it sets the udp port's up_ipaddr field). The second call to udp_put_data() simply sets the udp port's state to UPS_MAIN.

After the initialization (when the state of the udp port is UPS_MAIN), udp_put_data() is called only by ip_read() to handle the (unlikely) case that the underlying ip file descriptor was not already configured.


0039232          
0039233          case NWIOGIPIROUTE:
Copy an input routing table entry to a process. The entry number of the requested route is obtained from an nwio_route_t struct retrieved from the process and the routing entry data is also copied to the process in an nwio_route_t struct.

This case and the NWIOGIPOROUTE case probably should have been combined.


0039234                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0039235                            0, sizeof(nwio_route_t), TRUE);
The process is requesting a route in the input routing table. Determine which entry in the table the process is requesting. Note that the entry number is the only field in the nwio_route_t struct that is of interest here.

If the process opened an ip device file (e.g., /dev/ip) directly (which will almost certainly be the case), if_get_userdata is set to sr_get_userdata().


nwio_route_t


nwio_route_t is used to get a route for the input or output routing table from a process and to send a route in the input or output routing table to a process.

typedef struct nwio_route

{
u32_t nwr_ent_no;
u32_t nwr_ent_count;
ipaddr_t nwr_dest;
ipaddr_t nwr_netmask;
ipaddr_t nwr_gateway;
u32_t nwr_dist;
u32_t nwr_flags;
u32_t nwr_pref;
u32_t nwr_mtu; /* Ignored, compatibility with VMD */
ipaddr_t nwr_ifaddr;
} nwio_route_t;
u32_t nwr_ent_no: The entry number within the table.


u32_t nwr_ent_count: nwr_ent_count is the size of the routing table and is used only when returning a route to a process. If the process requested an input route, nwr_ent_count is IROUTE_NR (512) and if the process requested an output route, nwr_ent_count is OROUTE_NR (32).


ipaddr_t nwr_dest:

The network address of the routing entry (e.g., 192.30.1.0).

ipaddr_t nwr_netmask:

The subnet mask of the routing entry (e.g., 255.255.255.0).


ipaddr_t nwr_gateway:

The gateway to which packets destined for this network are sent.

u32_t nwr_dist:

The distance. Routes with low distances are chosen over routes with high distances.


u32_t nwr_flags:

If the route is an input route, nwr_flags can be one of the following:

#define IRTF_EMPTY 0
#define IRTF_INUSE 1
#define IRTF_STATIC 2

Static input routes behave differently than dynamic input routes. If a static route is added to the input routing table, the route will not not replace any pre-existing route (static or dynamic) even if the values of the route (e.g., destination network) are the same. A dynamic input route, on the other hand, will replace another dynamic input route.

If the route is an output route, nwr_flags can be one of the following:

#define ORTF_EMPTY 0
#define ORTF_INUSE 1
#define ORTF_STATIC 2


u32_t nwr_pref:

The preference of the route. Routes with high preferences are chosen over routes with low preferences. The distance of a route is of higher importance. In other words, the relative preference of two routes is only significant if and only if these two routes are tied for the lowest distance.


ipaddr_t nwr_ifaddr: Each route is associated with an ip port. nwr_ifaddr is the ip address of this ip port.


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.


0039236                   if (data == NULL)
If the nwio_route_t struct couldn't be obtained from the process, report the problem to the file system.


0039237                   {
0039238                            return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039239                                     EFAULT, NULL, TRUE);
0039240                   }
0039241 
0039242                   data= bf_packIffLess (data, sizeof(nwio_route_t) );
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039243                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039244                   ent_no= route_ent->nwr_ent_no;
0039245                   bf_afree(data);
We got the information we need. We can now free the accessors used to hold the nwio_route_t struct.


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).


0039246 
0039247                   data= bf_memreq(sizeof(nwio_route_t));
bf_memreq()

After the buffers have been initialized, accessors[] looks like the following:



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



Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.

So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.

For a complete description of the network service's buffer management, click here.


0039248                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039249                   result= ipr_get_iroute(ent_no, route_ent);
ipr_get_iroute()

ipr_get_iroute(ent_no, route_ent) returns the values of the ent_no (ipr_get_iroute()'s first parameter) element of the input routing table in route_ent (ipr_get_iroute()'s second parameter). Note that route_ent is a refernce to an nwio_route_t struct.

ipr_get_iroute() is called by ip_ioctl() (which is called by the pr_routes utility) for a NWIOGIPIROUTE (NetWork IO Get IP Input ROUTE) request.


0039250                   if (result < 0)
0039251                            bf_afree(data);
bf_afree()

After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:



Then the resulting chain will be:



bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.

bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).


0039252                   else
0039253                   {
0039254                            assert(result == NW_OK);
0039255                            result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0,
0039256                                     data, TRUE);
Give the process the input route.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039257                   }
0039258                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039259                            result, (acc_t *)0, TRUE);
Report to the file system whether the operation was successful.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039260 
0039261          case NWIOSIPIROUTE:
Add a route to the input routing table. The add_route utility can (indirectly) call ip_ioctl() with req set to NWIOSIPIROUTE to add an input route.


0039262                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0039263                            0, sizeof(nwio_route_t), TRUE);
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.


0039264                   if (data == NULL)
If the nwio_route_t struct couldn't be obtained from the process, report the problem to the file system.


0039265                   {
0039266                            return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039267                                     EFAULT, NULL, TRUE);
0039268                   }
0039269 
0039270                   data= bf_packIffLess (data, sizeof(nwio_route_t) );
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039271                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039272                   result= ipr_add_iroute(ip_fd->if_port->ip_port,
0039273                            route_ent->nwr_dest, route_ent->nwr_netmask,
0039274                            route_ent->nwr_gateway,
0039275                            (route_ent->nwr_flags & NWRF_UNREACHABLE) ?
0039276                                     IRTD_UNREACHABLE : route_ent->nwr_dist,
0039277                            !!(route_ent->nwr_flags & NWRF_STATIC), NULL);
ipr_add_iroute()

ipr_add_iroute(port_nr, dest, subnetmask, gateway, dist, static_route, iroute_p) adds an input route (with the values of the ipr_add_iroute()'s parameters) to the input routing table and removes any associated entries from the input route cache.

Note that static routes are handled differently than dynamic routes. A static input route does not replace any pre-existing route (static or dynamic) even if the values are the same. A dynamic input route, on the other hand, will replace another dynamic input route.

ipr_add_route() is called by ip_ioctl() when called by the add_route utility with the -i option.


0039278                   bf_afree(data);
bf_afree()

After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:



Then the resulting chain will be:



bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.

bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).


0039279 
0039280                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039281                            result, (acc_t *)0, TRUE);
Report to the file system whether the operation was successful.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


0039282 
0039283          case NWIOGIPOROUTE:
Copy an output routing table entry to a process. The entry number of the requested route is obtained from an nwio_route_t struct retrieved from the process and the routing entry data is also copied to the process in an nwio_route_t struct.

This case and the NWIOGIPIROUTE case probably should have been combined.


0039284                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0039285                            0, sizeof(nwio_route_t), TRUE);
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.nwio_route_t

nwio_route_t is used to get a route for the input or output routing table from a process and to send a route in the input or output routing table to a process.

typedef struct nwio_route

{
u32_t nwr_ent_no;
u32_t nwr_ent_count;
ipaddr_t nwr_dest;
ipaddr_t nwr_netmask;
ipaddr_t nwr_gateway;
u32_t nwr_dist;
u32_t nwr_flags;
u32_t nwr_pref;
u32_t nwr_mtu; /* Ignored, compatibility with VMD */
ipaddr_t nwr_ifaddr;
} nwio_route_t;
u32_t nwr_ent_no: The entry number within the table.


u32_t nwr_ent_count: nwr_ent_count is the size of the routing table and is used only when returning a route to a process. If the process requested an input route, nwr_ent_count is IROUTE_NR (512) and if the process requested an output route, nwr_ent_count is OROUTE_NR (32).


ipaddr_t nwr_dest:

The network address of the routing entry (e.g., 192.30.1.0).

ipaddr_t nwr_netmask:

The subnet mask of the routing entry (e.g., 255.255.255.0).


ipaddr_t nwr_gateway:

The gateway to which packets destined for this network are sent.

u32_t nwr_dist:

The distance. Routes with low distances are chosen over routes with high distances.


u32_t nwr_flags:

If the route is an input route, nwr_flags can be one of the following:

#define IRTF_EMPTY 0
#define IRTF_INUSE 1
#define IRTF_STATIC 2

Static input routes behave differently than dynamic input routes. If a static route is added to the input routing table, the route will not not replace any pre-existing route (static or dynamic) even if the values of the route (e.g., destination network) are the same. A dynamic input route, on the other hand, will replace another dynamic input route.

If the route is an output route, nwr_flags can be one of the following:

#define ORTF_EMPTY 0
#define ORTF_INUSE 1
#define ORTF_STATIC 2


u32_t nwr_pref:

The preference of the route. Routes with high preferences are chosen over routes with low preferences. The distance of a route is of higher importance. In other words, the relative preference of two routes is only significant if and only if these two routes are tied for the lowest distance.


ipaddr_t nwr_ifaddr: Each route is associated with an ip port. nwr_ifaddr is the ip address of this ip port.


0039286                   if (data == NULL)
If the nwio_route_t struct couldn't be obtained from the process, report the problem to the file system.


0039287                   {
0039288                            return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039289                                     EFAULT, NULL, TRUE);
0039290                   }
0039291 
0039292                   data= bf_packIffLess (data, sizeof(nwio_route_t) );
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039293                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039294                   ent_no= route_ent->nwr_ent_no;
0039295                   bf_afree(data);
bf_afree()

After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:



Then the resulting chain will be:



bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.

bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).


0039296 
0039297                   data= bf_memreq(sizeof(nwio_route_t));
bf_memreq()

After the buffers have been initialized, accessors[] looks like the following:



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



Note that three elements of accessors[] have been removed from buf512_freelist and that the head of the chain of the 3 accessors is returned by bf_memreq(). Also note that the acc_linkC and buf_linkC fields have been set to one and acc_length and acc_offset have been set to their appropriate values.

So what happens if there are not enough buffers on the buf512_freelist to satisfy a request? On lines 2280-2290 of buf.c, functions that free buffers for the specific clients (e.g., eth_buffree()) are called until there are enough buffers on buf512_freelist.

For a complete description of the network service's buffer management, click here.


0039298                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039299                   result= ipr_get_oroute(ent_no, route_ent);
ipr_get_oroute()

ipr_get_oroute(ent_no, route_ent) returns the values of the ent_no (ipr_get_oroute()'s first parameter) element of the main output routing table in route_ent (ipr_get_oroute()'s second parameter). Note that route_ent is a refernce to an nwio_route_t struct.

ipr_get_oroute() is called by ip_ioctl() (which is called by the pr_routes utility) for a NWIOGIPOROUTE (NetWork IO Get IP Output ROUTE) request.


0039300                   if (result < 0)
0039301                            bf_afree(data);
0039302                   else
0039303                   {
0039304                            assert(result == NW_OK);
0039305                            result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0,
0039306                                     data, TRUE);
Copy the route to the process.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039307                   }
0039308                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039309                            result, (acc_t *)0, TRUE);
Send a message to the file system indicating whether the operation was successful.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039310 
0039311          case NWIODIPIROUTE:
Delete an input routing table entry. The routing table entry is obtained from an nwio_route_t struct retrieved from the process and ipr_del_iroute() is called to delete the route.


0039312                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0039313                            0, sizeof(nwio_route_t), TRUE);
The process is attempting to delete a route in the input routing table. Get the route from the process.

If the process opened an ip device file (e.g., /dev/ip) directly (which will almost certainly be the case), if_get_userdata is set to sr_get_userdata().


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.


0039314                   if (data == NULL)
If the nwio_route_t struct couldn't be obtained from the process, report the problem to the file system.


0039315                   {
0039316                            return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039317                                     EFAULT, NULL, TRUE);
0039318                   }
0039319 
0039320                   data= bf_packIffLess (data, sizeof(nwio_route_t) );
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039321                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039322                   result= ipr_del_iroute(ip_fd->if_port->ip_port,
0039323                            route_ent->nwr_dest, route_ent->nwr_netmask,
0039324                            route_ent->nwr_gateway,
0039325                            (route_ent->nwr_flags & NWRF_UNREACHABLE) ?
0039326                                     IRTD_UNREACHABLE : route_ent->nwr_dist,
0039327                            !!(route_ent->nwr_flags & NWRF_STATIC));
ipr_del_iroute()

ipr_del_iroute(port_nr, dest, subnetmask, gateway, dist, static_route) eliminates from the main input routing table (i.e., iroute_table[][]) any routes whose port number, destination, subnet mask, gateway, distance and whether it's a static route match up with the parameters of ipr_del_iroute().


0039328                   bf_afree(data);
bf_afree()

After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:



Then the resulting chain will be:



bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.

bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).


0039329 
0039330                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039331                            result, (acc_t *)0, TRUE);
Send a message to the file system indicating whether the operation was successful.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039332 
0039333          case NWIOSIPOROUTE:
Add a route to the output routing table. The add_route utility can (indirectly) call ip_ioctl() with req set to NWIOSIPOROUTE to add an output route.

The route is obtained from an nwio_route_t struct retrieved from the process.


0039334                   data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
0039335                            0, sizeof(nwio_route_t), TRUE);
Get the route from the process.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_get_userdata field of the ip file descriptor was set to sr_get_userdata().


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.


nwio_route_t


nwio_route_t is used to get a route for the input or output routing table from a process and to send a route in the input or output routing table to a process.

typedef struct nwio_route

{
u32_t nwr_ent_no;
u32_t nwr_ent_count;
ipaddr_t nwr_dest;
ipaddr_t nwr_netmask;
ipaddr_t nwr_gateway;
u32_t nwr_dist;
u32_t nwr_flags;
u32_t nwr_pref;
u32_t nwr_mtu; /* Ignored, compatibility with VMD */
ipaddr_t nwr_ifaddr;
} nwio_route_t;
u32_t nwr_ent_no: The entry number within the table.


u32_t nwr_ent_count: nwr_ent_count is the size of the routing table and is used only when returning a route to a process. If the process requested an input route, nwr_ent_count is IROUTE_NR (512) and if the process requested an output route, nwr_ent_count is OROUTE_NR (32).


ipaddr_t nwr_dest:

The network address of the routing entry (e.g., 192.30.1.0).

ipaddr_t nwr_netmask:

The subnet mask of the routing entry (e.g., 255.255.255.0).


ipaddr_t nwr_gateway:

The gateway to which packets destined for this network are sent.

u32_t nwr_dist:

The distance. Routes with low distances are chosen over routes with high distances.


u32_t nwr_flags:

If the route is an input route, nwr_flags can be one of the following:

#define IRTF_EMPTY 0
#define IRTF_INUSE 1
#define IRTF_STATIC 2

Static input routes behave differently than dynamic input routes. If a static route is added to the input routing table, the route will not not replace any pre-existing route (static or dynamic) even if the values of the route (e.g., destination network) are the same. A dynamic input route, on the other hand, will replace another dynamic input route.

If the route is an output route, nwr_flags can be one of the following:

#define ORTF_EMPTY 0
#define ORTF_INUSE 1
#define ORTF_STATIC 2


u32_t nwr_pref:

The preference of the route. Routes with high preferences are chosen over routes with low preferences. The distance of a route is of higher importance. In other words, the relative preference of two routes is only significant if and only if these two routes are tied for the lowest distance.


ipaddr_t nwr_ifaddr: Each route is associated with an ip port. nwr_ifaddr is the ip address of this ip port.


0039336                   if (data == NULL)
If the nwio_route_t struct couldn't be obtained from the process, report the problem to the file system.


0039337                   {
0039338                            return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039339                                     EFAULT, NULL, TRUE);
0039340                   }
0039341 
0039342                   data= bf_packIffLess (data, sizeof(nwio_route_t) );
bf_packIffLess()

If the data in a linked list of accessors is less than min_len (the second parameter), bf_packIffLess(pack, min_len) packs the data by calling bf_pack().

bf_packIffLess() is often called to ensure that a packet's header is in a single contiguous buffer so that the individual fields of the header can be easily accessed.

For a detailed description of the network service's buffer management, click here.


0039343                   route_ent= (nwio_route_t *)ptr2acc_data(data);
ptr2acc_data()

The macro ptr2acc_data is #define'd in inet/generic/buf.h as:

#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \
(&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \
acc_offset]))

ptr2acc_data() simply returns a pointer to the actual data within an accessor.

ptr2acc_data() is usually called so that the fields of a header (e.g., ip header) can be analyzed.


0039344                   result= ipr_add_oroute(ip_fd->if_port->ip_port,
0039345                            route_ent->nwr_dest, route_ent->nwr_netmask,
0039346                            route_ent->nwr_gateway, (time_t)0,
0039347                            route_ent->nwr_dist,
0039348                            !!(route_ent->nwr_flags & NWRF_STATIC),
0039349                            route_ent->nwr_pref, NULL);
ipr_add_oroute()

ipr_add_oroute() adds an output route to the main output routing table and, if successful, returns a reference to the new entry in the last parameter (if not NULL). ipr_add_oroute() finds either an empty entry, an expired entry, an entry with the same port, network, subnet mask, and smaller distance, or the oldest entry to use for a new dynamic route. Only an empty entry, an expired entry, or the oldest entry can be used for a new static route.

For a detailed description of the layout of the main output routing table (and specifically, the nextnw, nextgw, and nextdist fields), click here.


0039350                   bf_afree(data);
bf_afree()

After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:



Then the resulting chain will be:



bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.

bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).


0039351 
0039352                   return (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
0039353                            result, (acc_t *)0, TRUE);
Send a message to the file system indicating whether the operation was successful.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039354 
0039355          default:
0039356                   break;
0039357          }
0039358          DBLOCK(1, printf("replying EBADIOCTL\n"));
0039359          return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, EBADIOCTL,
0039360                   (acc_t *)0, TRUE);
The ioctl request was invalid. Report the problem to the file system.

If the process opened an ip device file (e.g., /dev/ip) directly, the if_put_userdata field of the ip file descriptor was set to sr_put_userdata().


sr_put_userdata()


sr_put_userdata(fd, offset, data, for_ioctl) is the counterpart to sr_get_userdata() and (like sr_get_userdata()) does one of two things:

1) Copies data from a buffer (to be more specific, a chain of accessors) within the network service (this process) to a buffer within the user process. This can be either ioctl data (in which case, for_ioctl is TRUE) or read/write data (for_ioctl is FALSE). For example, udp_ioctl() (indirectly) calls sr_put_userdata() to give configuration data to a user process. Also, udp_packet2user() (indirectly) calls sr_get_userdata() to pass data to the user process.

2) Sends a message to the FS. For example, if a read is attempted on a udp file descriptor before the file descriptor is configured, reply_thr_put() is called, which then (indirectly) calls sr_put_userdata(), passing in EBADMODE for the parameter count.

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


0039361 }
0039362 
0039363 PUBLIC void ip_hash_proto(ip_fd)
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.


0039364 ip_fd_t *ip_fd;
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).


0039365 {
0039366          ip_port_t *ip_port;
0039367          int hash;
0039368 
0039369          ip_port= ip_fd->if_port;
0039370          if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY)
The ip file descriptor accepts packets of any protocol.


0039371          {
0039372                   ip_fd->if_proto_next= ip_port->ip_proto_any;
0039373                   ip_port->ip_proto_any= ip_fd;
0039374          }
0039375          else
The ip file descriptor accepts only packets of a specific protocol.


0039376          {
0039377                   hash= ip_fd->if_ipopt.nwio_proto & (IP_PROTO_HASH_NR-1);
IP_PROTO_HASH_NR is #define'd as 32 in ip_int.h. Therefore, packets of protocol 17 (UDP) will share ip_proto[17] with packets of protocol of 49.


0039378                   ip_fd->if_proto_next= ip_port->ip_proto[hash];
0039379                   ip_port->ip_proto[hash]= ip_fd;
0039380          }
0039381 }
0039382 
0039383 PUBLIC void ip_unhash_proto(ip_fd)
0039384 ip_fd_t *ip_fd;
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).


0039385 {
0039386          ip_port_t *ip_port;
0039387          ip_fd_t *prev, *curr, **ip_fd_p;
0039388          int hash;
0039389 
0039390          ip_port= ip_fd->if_port;
Find the linked list that contains the ip file descriptor (lines 39391-39399).


0039391          if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY)
The ip file descriptor accepts packets of any protocol.


0039392          {
0039393                   ip_fd_p= &ip_port->ip_proto_any;
0039394          }
0039395          else
The ip file descriptor accepts only packets of a specific protocol.


0039396          {
0039397                   hash= ip_fd->if_ipopt.nwio_proto & (IP_PROTO_HASH_NR-1);
0039398                   ip_fd_p= &ip_port->ip_proto[hash];
0039399          }
At this point, the linked list that contains the file descriptor has been found.

Find the ip file descriptor in this linked list (lines 39400-39405) and remove it from the linked list (lines 39407-39410).


0039400          for (prev= NULL, curr= *ip_fd_p; curr;
0039401                   prev= curr, curr= curr->if_proto_next)
0039402          {
0039403                   if (curr == ip_fd)
0039404                            break;
0039405          }
0039406          assert(curr);
0039407          if (prev)
0039408                   prev->if_proto_next= curr->if_proto_next;
0039409          else
0039410                   *ip_fd_p= curr->if_proto_next;
0039411 }
0039412 
0039413 PRIVATE int ip_checkopt (ip_fd)
0039414 ip_fd_t *ip_fd;
ip_checkopt()

ip_checkopt() checks the validity of an ip file descriptor's options. For example, ip_checkopt() verifies that one flag (disable or enable) is set in each set of flags (e.g., the NWIO_ACC_MASK set of flags). ip_checkopt() also verifies that the ip header options are valid.

If the flags are acceptable, the ip file descriptor is placed in its corresponding ip port's appropriate linked list.


0039415 {
0039416 /* bug: we don't check access modes yet */
0039417 
0039418          unsigned long flags;
0039419          unsigned int en_di_flags;
0039420          ip_port_t *port;
0039421          acc_t *pack;
0039422          int result;
0039423 
0039424          flags= ip_fd->if_ipopt.nwio_flags;
0039425          en_di_flags= (flags >>16) | (flags & 0xffff);
en_di_flags is the result of OR'ing the ip file descriptor's enabled and disabled flags.


0039426 
0039427          if (flags & NWIO_HDR_O_SPEC)
0039428          {
0039429                   result= ip_chk_hdropt (ip_fd->if_ipopt.nwio_hdropt.iho_data,
0039430                            ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz);
ip_chk_hdropt()

ip_chk_hdropt() goes through the ip header options (if there are any) and verifies that the options are acceptable. For example, ip_chk_hdropt() verifies that the same ip header option is not listed twice.


0039431                   if (result<0)
0039432                            return result;
0039433          }
Verify that at least one flag (enable or disable) in each group of flags (e.g., NWIO_ACC_MASK) is set. If so, mark the ip file descriptor as having its options configured (IFF_OPSET for the file descriptor is set) and place the file descriptor in its corresponding ip port's appropriate linked list.


0039434          if ((en_di_flags & NWIO_ACC_MASK) &&
0039435                   (en_di_flags & NWIO_LOC_MASK) &&
0039436                   (en_di_flags & NWIO_BROAD_MASK) &&
0039437                   (en_di_flags & NWIO_REM_MASK) &&
0039438                   (en_di_flags & NWIO_PROTO_MASK) &&
0039439                   (en_di_flags & NWIO_HDR_O_MASK) &&
0039440                   (en_di_flags & NWIO_RW_MASK))
0039441          {
0039442                   ip_fd->if_flags |= IFF_OPTSET;
0039443 
0039444                   ip_hash_proto(ip_fd);
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).


0039445          }
0039446 
0039447          else
0039448                   ip_fd->if_flags &= ~IFF_OPTSET;
0039449 
If any packets arrived at this ip file descriptor while it was unconfigured (or while it had a different configuration), get rid of of the packets.


0039450          while (ip_fd->if_rdbuf_head)
0039451          {
0039452                   pack= ip_fd->if_rdbuf_head;
0039453                   ip_fd->if_rdbuf_head= pack->acc_ext_link;
0039454                   bf_afree(pack);
0039455          }
0039456          return NW_OK;
0039457 }
0039458 
0039459 PRIVATE void reply_thr_get(ip_fd, reply, for_ioctl)
0039460 ip_fd_t *ip_fd;
0039461 size_t reply;
0039462 int for_ioctl;
reply_thr_get() / ip

ip's reply_thr_get() is very similar to udp's reply_thr_get().

reply_thr_get() calls (indirectly) a function from a higher layer (e.g., sr_get_userdata()), udp_ip_arrived(). If sr_get_userdata() is called, it reports to a user process whether a previous operation requested by the user process was successful. reply, reply_thr_get()'s second parameter, indicates whether the previous operation was successful.

After sending the message to the user process, sr_get_userdata() processes the messages in the write or ioctl queue.


0039463 {
0039464          acc_t *result;
0039465          result= (ip_fd->if_get_userdata)(ip_fd->if_srfd, reply,
0039466                   (size_t)0, for_ioctl);
The if_get_userdata field of an ip file descriptor is set by ip_open(). If the udp code opens the file, if_get_userdata references udp_ip_arrived().


0039467          assert (!result);
0039468 }
0039469 
0039470 /*
0039471  * $PchId: ip_ioctl.c,v 1.8 1996/12/17 07:56:18 philip Exp $
0039472  */