On Tue, Mar 22, 2022 at 02:24:25PM +1000, David Gwynne wrote:
> i couldnt find any good examples of what to do when you wanted to
> receive multiple control messages from a single recvmsg call. the most
> interesting bit is how much space the buffer needs to be.
>
> if i struggled maybe someone else will too?
>
> Index: CMSG_DATA.3
> ===================================================================
> RCS file: /cvs/src/share/man/man3/CMSG_DATA.3,v
> retrieving revision 1.6
> diff -u -p -r1.6 CMSG_DATA.3
> --- CMSG_DATA.3 3 Apr 2017 19:40:43 -0000 1.6
> +++ CMSG_DATA.3 22 Mar 2022 04:23:50 -0000
> @@ -116,7 +116,8 @@ if (sendmsg(s, &msg, 0) == -1)
> err(1, "sendmsg");
> .Ed
> .Pp
> -And an example that receives and decomposes the control message:
> +The following example receives and decomposes a control message
> +containing a file descriptor:
> .Bd -literal -offset indent
> struct msghdr msg;
> struct cmsghdr *cmsg;
> @@ -146,6 +147,62 @@ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg !=
> cmsg->cmsg_type == SCM_RIGHTS) {
> fd = *(int *)CMSG_DATA(cmsg);
> /* Do something with the descriptor. */
> + }
> +}
> +.Ed
> +.Pp
> +The following example shows how to to receive multiple control
> +messages for a single datagram.
> +In this example a program is receiving an IPv4 UDP datagram
> +using a socket that has been configured to provide the local
> +(destination) IP address and port using
> +.Xr setsockopt 2
> +and the
> +.Dv IP_RECVDSTADDR
> +and
> +.Dv IP_RECVDSTADDR
I guess this should be IP_RECVDSTPORT
> +.Xr ip 4
> +options respectively.
> +.Bd -literal -offset indent
> +struct msghdr msg;
> +struct cmsghdr *cmsg;
> +union {
> + struct cmsghdr hdr;
> + unsigned char buf[CMSG_SPACE(sizeof(struct in_addr)) +
> + CMSG_SPACE(sizeof(in_port_t))];
> +} cmsgbuf;
Should we add a comment that this union is used for proper alignment of
the buffer?
> +struct sockaddr_in sin;
> +struct iovec io_vector[1];
> +
> +sin.sin_family = AF_INET;
> +
> +io_vector[0].iov_base = &ch;
> +io_vector[0].iov_len = 1;
> +
> +memset(&msg, 0, sizeof(msg));
> +msg.msg_control = &cmsgbuf.buf;
> +msg.msg_controllen = sizeof(cmsgbuf.buf);
> +msg.msg_iov = io_vector;
> +msg.msg_iovlen = 1;
> +
> +if (recvmsg(s, &msg, 0) == -1)
> + err(1, "recvmsg");
> +if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC))
> + errx(1, "control message truncated");
> +for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
> + cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> + if (cmsg->cmsg_len == CMSG_LEN(sizeof(struct sockaddr_in)) &&
> + cmsg->cmsg_level == IPPROTO_IP &&
> + cmsg->cmsg_type == IP_RECVDSTADDR) {
> + sin.sin_addr = *(struct in_addr *)CMSG_DATA(cmsg);
> + continue;
> + }
> +
> + if (cmsg->cmsg_len == CMSG_LEN(sizeof(in_port_t)) &&
> + cmsg->cmsg_level == IPPROTO_IP &&
> + cmsg->cmsg_type == IP_RECVDSTPORT) {
> + sin.sin_port = *(in_port_t *)CMSG_DATA(cmsg);
> + continue;
> }
Not sure but IIRC some code uses switch statements here:
if (cmsg->cmsg_level == IPPROTO_IP)
switch (cmsg->cmsg_type) {
case IP_RECVDSTADDR:
if (cmsg->cmsg_len ==
CMSG_LEN(sizeof(struct sockaddr_in))
sin.sin_addr =
*(struct in_addr *)CMSG_DATA(cmsg);
break;
> }
> .Ed
>
The fd passing example in that manpage is missing a few extra checks.
Mainly the fact that more than one fd could be received on 64bit archs.
We should probably update that example using the code from imsg.c
--
:wq Claudio