After opening a channel, read, write and ioctl requests to network device files (e.g., /dev/udp) can be made by user processes. Below is a description of a request by a user process to write to the device file "/dev/udp". In this case, /dev/udp is associated with an ethernet device (as opposed to a psip device).
Note that the network service must be configured with the ifconfig utility (if the RARP service is not configured) and the add_route utility before anything meaningful can occur (both utilities are generally called in start-up scripts). Also note that before a write to a udp device file can be made, the udp device file must be configured.
To make a write request, a user process uses the write() system call (defined in src/lib/posix/_write.c). write() has the following prototype:
ssize_t write(fd, buffer, nbytes)
write() takes these parameters and builds and sends a messsage to the file system.
(User processes never send messages directly to the network service; messages are sent to the file system and the file system then sends a message to the network service.)
This message contains the following information:
fd (file descriptor)
nbytes (requested number of bytes to be written)
buffer (location of the data to be written)
the source of the message (the user process making the request)
type (set to WRITE)
The file system's endless loop (found in main(), which is in src/fs/main.c), processes the message, eventually calling do_write(), which calls read_write() (src/fs/read.c), which calls dev_io(). dev_io() builds a message and then calls gen_io() to deliver a message (also a write request but different than the first message) to the network service.
So what does the network service do with the message it receives from the file system? sr_rec()is called within the endless loop within main() to process the message (all messages from the file system are processed by sr_rec()). If there aren't any messages that are waiting to be processed, sr_rec() calls sr_rwio() (sr_rwio() handles read, write, and io requests). sr_rwio() then calls the protocol-specific write function, which in this case is udp_write(). The most significant thing that udp_write() does is to call restart_write_fd().
Things become interesting in restart_write_fd(). restart_write_fd() first gets the data (likely containing a pseudo udp header) from the user process.
restart_write_fd() then strips the pseudo udp header and adds a (normal) udp header and an ip header.Next, restart_write_fd() passes the packet to the next lower layer (i.e., the ip code) by calling ip_write(), which calls ip_send() to do most of its work. ip_send() sets some of the ip header's ip options and then either places the ip packet in the loopback queue if the packet is destined for the loopback address or the ip address of a local port, calls ipeth_send() to send the packet out the ethernet interface if the destination of the packet is in the same network as the local system, or calls oroute_frag() to determine the optimal router to which to send the packet. If the destination of the packet is in the same network as the local system, ipeth_send() first prepends an ethernet header. Next, ipeth_send() calls eth_write(), which calls eth_send() if there are no ethernet packets waiting to be sent to the ethernet task (driver). eth_send(), in turn, calls eth_write_port(), which sends a message (also a write request but different than the previous two messages) to the ethernet task indicating that an ethernet packet is ready to be sent.
After receiving a reply from the ethernet task indicating that the packet has been sent:
sr_rec() sends a message back to the file system indicating that the write request has been completed.
The file system then relays this information (with yet another message) back to the user process that made the request.