CMSG_DATA(3)

๐Ÿ“ฆ version 15.1, in freebsd 14.5, freebsd 15.1

๐Ÿ’… 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 header cmsg. 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 message msghdr. And if there are no control messages attached? It returns NULL. 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. len is the length of the data held by the control message. This value is what's normally stored in the cmsg_len of 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 following cmsg in the message msghdr. And if cmsg is 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 length len, and yeah it includes the control message header too. This value is what's normally stored in msg_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