November 16, 2022

BSD TCP/IP for Kyu - write and sendmsg

I now have bind working and am able to establish a connection and could read or write to it if I had that part of the software working.

Interestingly there are several ways to write to a socket on a BSD system and it is important for me to understand those to be able to adapt the code to Kyu.

It turns out that write() and send() are the same, except that send() gives the ability to add flags (which are all zero for write(). The only one that I can imagine using is the one to send OOB (out of bounds) messages.

There is also sendto(), which is identical to send() if you have a connection. It allows an address to be specified (which might be useful for UDP, but seems of no use for TCP).

Finally there is sendmsg(). Here instead of giving "buf" and "len" you pass a msghdr structure which looks like this:

      struct msghdr {
               void         *msg_name;       /* Optional address */
               socklen_t     msg_namelen;    /* Size of address */
               struct iovec *msg_iov;        /* Scatter/gather array */
               size_t        msg_iovlen;     /* # elements in msg_iov */
               void         *msg_control;    /* Ancillary data, see below */
               size_t        msg_controllen; /* Ancillary data buffer len */
               int           msg_flags;      /* Flags (ignored) */
           };
Here the big addition is having the "iovec" structure which could be a scatter/gather array. You also get the option to include "control" data, which might be IP options. Note that the flags are ignored, so you don't get to use flags along with scatter/gather.

In the code (kern/uipc_syscalls.c) this gets packaged up in a "uio" structure like this:

struct iovec {
        char    *iov_base;      /* Base address. */
        size_t   iov_len;       /* Length. */
};

struct uio {
        struct  iovec *uio_iov;
        int     uio_iovcnt;
        off_t   uio_offset;
        int     uio_resid;
        enum    uio_seg uio_segflg;
        enum    uio_rw uio_rw;
};

	auio.uio_iov = mp->msg_iov;
        auio.uio_iovcnt = mp->msg_iovlen;
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_rw = UIO_WRITE;
        auio.uio_offset = 0;
        auio.uio_resid = 0;
We eventually end up in the routine "sosend":
sosend ( so, addr, uio, top, control, flags)
sosend ( so, 0, uio, 0, 0, 0 )
So for us, all we care about in sosend is the "uio" structure. And the uio itself gets pretty much handled by:
error = uiomove(mtod(m, caddr_t), (int)len, uio);
Here data is being copied from user space into a kernel mbuf. The routine uiomove() is in kern/kern_subr.c


Have any comments? Questions? Drop me a line!

Kyu / [email protected]