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); }