2

I am working on a quite complex C program, designed to run under Linux, which is relying a lot on ancillary data coming from sockets.

I am actually experiencing a strange behaviour, which seem to occur, by the way, on my program only (other sample codes, like this one, are working properly). I'm using ancillary data by defining:

struct msghdr mhdr;
struct iovec iov;
struct cmsghdr *cmsg = NULL;
struct sockaddr_ll addrll;
socklen_t addrllLen=sizeof(addrll);
unsigned char packet[PACKET_SIZE_MAX];
char ctrlBuf[CMSG_SPACE(sizeof(struct scm_timestamping))]; // For example, in order to retrieve timestamps

Then, by setting:

    memset(&mhdr,0,sizeof(mhdr));

    iov.iov_base=packet;
    iov.iov_len=sizeof(packet);

    mhdr.msg_name=&(addrll); // This is a raw socket
    mhdr.msg_namelen=addrllLen; // This is a raw socket

    mhdr.msg_control=ctrlBuf;
    mhdr.msg_controllen=sizeof(ctrlBuf);

    mhdr.msg_iov=&iov;
    mhdr.msg_iovlen=1;

    mhdr.msg_flags=0;

I then send and receive data using sendto() and recvmsg(), after setting some socket options to retrieve useful ancillary data (such as software or hardware timestamps, by setting SO_TIMESTAMPING). I was able to verify that these options are actually supported by the sockets I am opening.

The issue sometimes comes up when I try to extract the ancillary data, using a code like the following one, which is, for instance, extracting a struct scm_timestamping with transmit/receive timestamps:

for(cmsg=CMSG_FIRSTHDR(&mhdr);cmsg!=NULL;cmsg=CMSG_NXTHDR(&mhdr, cmsg)) {
    if(cmsg->cmsg_level==SOL_SOCKET && cmsg->cmsg_type==SO_TIMESTAMPING) {
        hw_ts=*((struct scm_timestamping *)CMSG_DATA(cmsg));
    }
}

Sometimes, CMSG_FIRSTHDR(&mhdr) returns a null pointer and the returned mhdr.msg_controllen is 0, in such a way that I cannot extract any timestamp. Other times, on the same PC and using the same NIC, everything is perfectly fine.

So, my question is: in which cases can the returned mhdr.msg_controllen be 0, in general? Can this be due to a problem in the definition of the struct msghdr? Or is this due to some sort of kernel problem?

es483
  • 361
  • 2
  • 16

1 Answers1

0

It's likely that your control message buffer is undersized, since it's not aligned and the part below the first alignment boundary will be unusable. I'm not sure what the right idiomatic way to setup the buffer is (probably using malloc is the only way to avoid code that, strictly speaking, is UB due to alignment and effective-type/"aliasing" violations), but adding _Alignof(max_align_t) (or sizeof(max_align_t)) to the buffer size is probably one solution. I'd like to know what the accepted "right" way to do this is, though.

The cmsg(3) man page seems to recommend a union for alignment:

      union {         /* Ancillary data buffer, wrapped in a union
                         in order to ensure it is suitably aligned */
          char buf[CMSG_SPACE(sizeof(myfds))];
          struct cmsghdr align;
      } u;
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 2
    Thank you for your reply! I tried forcing some alignment, but, unfortunately, that did not solve the problem, as the situation remained unchanged. Is the alignment needed also for receiving CSMG data (where the buffer should be allocated, but it is actually filled and managed by `recvmsg()`, as in my case), other than for sending it (as in the example you reported)? Even if I try to oversize my buffers, the result, when the issue occurs, is the same: `mhdr.msg_controllen` is `0` and I cannot extract any ancillary data. – es483 Jul 25 '19 at 10:04