Skip to content
Advertisement

Understanding the msghdr structure from sys/socket.h

I’m trying to understand the following members of the msghdr structure of the sys/socket.h lib.

  • struct iovec *msg_iov scatter/gather array
  • void *msg_control ancillary data, see below

It states below:

Ancillary data consists of a sequence of pairs, each consisting of a cmsghdr structure followed by a data array. The data array contains the ancillary data message, and the cmsghdr structure contains descriptive information that allows an application to correctly parse the data.


I’m assuming the msghdr struct, contains the protocol-header information? if so… *msg_iov is the input/output “vector” of parameters in the request/response? and the *msg_control contains the response messages?

Advertisement

Answer

msg_iov is an array of input/output buffers with length msg_iovlen. Each member of this array contains a pointer to a data buffer and the size of the buffer. This is where the data to read/write lives. It allows you to read/write to an array of buffers which are not necessarily in contiguous memory regions.

msg_control points to a buffer of size msg_controllen that contains additional information about the packet. To read this field, you first need to declare a struct cmsghdr * (let’s call it cmhdr). You populate this by calling CMSG_FIRSTHDR() the first time, passing it the address of the msghdr struct, and CMSG_NXTHDR() each subsequent time, passing it the address of the msghdr struct and the current value of cmhdr.

From the msg_control, you can find interesting things like the destination IP of the packet (useful for multicast) and the contents of the TOS/DSCP byte in the IP header (useful for custom congestion control protocols), among others. In most cases, you’ll need to make a setsockopt call to enable receiving this data. In the examples given, the IP_PKTINFO and IP_TOS options need to be enabled.

See the cmsg(3) manpage for more details.

The source IP and port, are not in msg_control, but are in msg_name which expects a pointer to a struct sockaddr with length msg_namelen.

Here’s an example of how to use this:

struct msghdr mhdr;
struct iovec iov[1];
struct cmsghdr *cmhdr;
char control[1000];
struct sockaddr_in sin;
char databuf[1500];
unsigned char tos;

mhdr.msg_name = &sin
mhdr.msg_namelen = sizeof(sin);
mhdr.msg_iov = iov;
mhdr.msg_iovlen = 1;
mhdr.msg_control = &control;
mhdr.msg_controllen = sizeof(control);
iov[0].iov_base = databuf;
iov[0].iov_len = sizeof(databuf);
memset(databuf, 0, sizeof(databuf));
if ((*len = recvmsg(sock, &mhdr, 0)) == -1) {
    perror("error on recvmsg");
    exit(1);
} else {
    cmhdr = CMSG_FIRSTHDR(&mhdr);
    while (cmhdr) {
        if (cmhdr->cmsg_level == IPPROTO_IP && cmhdr->cmsg_type == IP_TOS) {
            // read the TOS byte in the IP header
            tos = ((unsigned char *)CMSG_DATA(cmhdr))[0];
        }
        cmhdr = CMSG_NXTHDR(&mhdr, cmhdr);
    }
    printf("data read: %s, tos byte = %02Xn", databuf, tos); 
}
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement