CMSG_DATA(3)
๐ CMSG_DATA(3) โ socket control message routines for ancillary data access
okay bestie, so this is page 3 of the Library Functions Manual โ the manual name is literally right there in the corner, no cap โ and today we're doing the socket control message girlies. Ancillary data access. Yes it sounds scary. No it's not, come sit with me ๐ซถ
CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE โ socket control message routines for ancillary data access. That's the whole friend group โจ
โจ SYNOPSIS โจ
#include <sys/socket.h>
unsigned char *
CMSG_DATA(struct cmsghdr *);
struct cmsghdr *
CMSG_FIRSTHDR(struct msghdr *);
size_t
CMSG_LEN(size_t);
struct cmsghdr *
CMSG_NXTHDR(struct msghdr *, struct cmsghdr *);
size_t
CMSG_SPACE(size_t);
๐ฆ DESCRIPTION ๐ฆ
So the control message API is the thing you use to construct ancillary data objects for control messages that get sent and received across sockets. It's giving backstage-pass energy ๐๏ธ
Control messages get passed around by the recvmsg(2) and sendmsg(2) system calls. The cmsghdr structure โ which is described over in recvmsg(2), go read it, iykyk โ is what you use to specify a chain of control messages.
Use these routines, bestie. Do not go poking at the control message header members and data buffers directly โ these routines are the ones that make sure the necessary alignment constraints are met. Trust the process ๐
The following routines are provided:
CMSG_DATA(cmsg)โ this one accesses the data portion of the control message headercmsg. And the way it ensures proper alignment constraints on the beginning of ancillary data are met? iconic. you literally could not do that yourself.CMSG_FIRSTHDR(msghdr)โ accesses the first control message attached to the messagemsghdr. And if there are no control messages attached? It returnsNULL. empty. barren. no messages. ๐ญCMSG_LEN(len)โ figures out the size in bytes of a control message, and yes that includes the control message header.lenis the length of the data held by the control message. This value is what's normally stored in thecmsg_lenof each control message. It also accounts for any alignment constraints on the beginning of ancillary data โ she's thorough ๐CMSG_NXTHDR(msghdr, cmsg)โ returns the location of the control message followingcmsgin the messagemsghdr. And ifcmsgis the last one in the chain?NULL. end of the line. that's the last one, periodt.CMSG_SPACE(len)โ determines the size in bytes needed to hold a control message and its contents of lengthlen, and yeah it includes the control message header too. This value is what's normally stored inmsg_msgcontrollen. It accounts for any alignment constraints on the beginning of ancillary data as well as any needed to pad the next control message. she does it all. multi-talented. ๐
๐ EXAMPLES ๐
okay so the example: it constructs a control message containing a file descriptor in the parent process, passes it over a pre-shared socket to the child process, and then the child sends a "hello" string back to the parent using the received file descriptor. full circle communication. we love a reciprocal relationship ๐ซถ
#include <sys/socket.h>
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#define HELLOLEN sizeof("hello")
int
main()
{
struct msghdr msg;
union {
struct cmsghdr hdr;
unsigned char buf[CMSG_SPACE(sizeof(int))];
} cmsgbuf;
char buf[HELLOLEN];
int hellofd[2];
int presharedfd[2];
struct cmsghdr *cmsg;
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, presharedfd) == -1)
err(EX_OSERR, "failed to create a pre-shared socket pair");
memset(&msg, 0, sizeof(msg));
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
msg.msg_iov = NULL;
msg.msg_iovlen = 0;
switch (fork()) {
case -1:
err(EX_OSERR, "fork");
case 0:
close(presharedfd[0]);
strlcpy(buf, "hello", HELLOLEN);
if (recvmsg(presharedfd[1], &msg, 0) == -1)
err(EX_IOERR, "failed to receive a message");
if (msg.msg_flags & (MSG_CTRUNC | MSG_TRUNC))
errx(EX_IOERR, "control message truncated");
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
hellofd[1] = *(int *)CMSG_DATA(cmsg);
printf("child: sending '%s'\n", buf);
if (write(hellofd[1], buf, HELLOLEN) == -1)
err(EX_IOERR, "failed to send 'hello'");
}
}
break;
default:
close(presharedfd[1]);
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, hellofd) == -1)
err(EX_OSERR, "failed to create a 'hello' socket pair");
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = hellofd[1];
if (sendmsg(presharedfd[0], &msg, 0) == -1)
err(EX_IOERR, "sendmsg");
close(hellofd[1]);
if (read(hellofd[0], buf, HELLOLEN) == -1)
err(EX_IOERR, "failed to receive 'hello'");
printf("parent: received '%s'\n", buf);
break;
}
return (0);
}
๐ SEE ALSO ๐
recvmsg(2), sendmsg(2), socket(2), ip(4), ip6(4), unix(4)
go read those too, they're all linked, it's a whole ecosystem bestie ๐ฆ
โญ STANDARDS โญ
- W. Stevens and M. Thomas, Advanced Sockets API for IPv6, RFC 2292, February 1998.
- W. Stevens, M. Thomas, E. Nordmark, and T. Jinmei, Advanced Sockets Application Program Interface (API) for IPv6, RFC 3542, May 2003.
two RFCs. practically vintage. ๐
๐ง HISTORY ๐ง
The control message API first appeared in 4.2BSD. and this manual page itself? originally written by Jared Yanovich <jaredy@OpenBSD.org> for OpenBSD 3.8, and eventually brought to FreeBSD 12.0 by Mateusz Piotrowski <0mp@FreeBSD.org>. a whole journey. a whole lore arc. we stan a documented migration ๐
aaaand that's the page, bestie ๐ sign-off time: this one's from Debian, dated March 13, 2020 โ CMSG_DATA(3), that's a wrap. slay ๐
โจ
๐ฆ where this page comes from
- freebsd 14.5: base 14.5
- freebsd 15.1: base 15.1
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms