0039001 /*
0039002 ip_ioctl.c
0039003
0039004 Copyright 1995 Philip Homburg
0039005 */
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)
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;
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;
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)
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)
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)
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;
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)
0039371 {
0039372 ip_fd->if_proto_next= ip_port->ip_proto_any;
0039373 ip_port->ip_proto_any= ip_fd;
0039374 }
0039375 else
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;
0039391 if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY)
0039392 {
0039393 ip_fd_p= &ip_port->ip_proto_any;
0039394 }
0039395 else
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);
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 */
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.