Understanding the msghdr structure from sys/socket.h
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);
}
Related videos on Youtube
Jordan Davis
If you rely on frameworks don't state you know how to code, because if you did, you wouldn't be using one.
Updated on June 18, 2022Comments
-
Jordan Davis almost 2 years
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? -
Jordan Davis over 8 yearswhy is
struct sockaddr_in
? shouldn't it bestruct sockaddr
? -
dbush over 8 years
struct sockaddr
is a generic type for getting socket info. For an IPv4 socket, the specific type isstruct sockaddr_in
. For a UNIX domain socket, it'sstruct sockaddr_un
, and for an IPv6 socket, it'sstruct sockaddr_in6
-
Jordan Davis over 8 yearsHaha okay. Where the hell do you see that though lol? I don't even see in the spec at all.
-
dbush over 8 years
man 7 ip
,man 7 unix
, andman 7 ipv6
, as well asman 7 socket
. -
Jordan Davis over 8 yearsOk yea got it now... they really should put that shit in the main spec or at least put it in comments rather than declaring a generic type, because you can't even use that generic type, right?