On Tue, Mar 22, 2022 at 09:12:23AM +0100, Claudio Jeker wrote:
> 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
yep, sorry.
> > +.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?
it seems strange to me that the main text talks about correctly aligning
the data, but fails to mention that care must taken to also align the
whole buffer. eg this:
These routines should be used instead of directly accessing the control
message header members and data buffers as they ensure that necessary
alignment constraints are met.
i feel like it should mention that this only works if it's properly
aligned in the first place.
adding a comment or even more text to the examples about using the union
to provide that alignment wouldnt hurt either.
> > +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;
yes, i do that in the actual code i was working on, but it felt too
clever for the manpage.
i feel like i need to go back and add length checks to some of my code
now though.
> > }
> > .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
wouldnt that be better discussed in the unix^Wsocket^Wrecvmsg manpage?