Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 493
Jordan Davis Avatar asked Sep 15 '15 19:09

Jordan Davis


People also ask

What does Sys socket H do?

The <sys/socket. h> header defines the following macros to gain access to the data arrays in the ancillary data associated with a message header: CMSG_DATA(cmsg) If the argument is a pointer to a cmsghdr structure, this macro returns an unsigned character pointer to the data array associated with the cmsghdr structure.

Where is SYS socket H in Linux?

On Debian and it's derivates (Ubuntu, ...) are basic header files (including sys/socket. h ) in package libc6-dev . The place under /usr/include is arch dependent.


1 Answers

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 = %02X\n", databuf, tos); 
}
like image 106
dbush Avatar answered Oct 21 '22 09:10

dbush