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

Reply via email to