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