c - 在从 Linux 套接字发送/接收数据包后访问辅助数据时,在哪些情况下 msg_controllen 可以为 0?

标签 c linux sockets linux-kernel timestamp

我正在开发一个非常复杂的 C 程序,旨在在 Linux 下运行,它在很大程度上依赖于来自套接字的辅助数据。

我实际上遇到了一种奇怪的行为,顺便说一下,这种行为似乎只发生在我的程序中(其他示例代码,如 this one,工作正常)。 我通过定义使用辅助数据:

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

然后,通过设置:

    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;

然后,在设置一些套接字选项以检索有用的辅助数据(例如软件或硬件时间戳,通过设置 SO_TIMESTAMPING)。我能够验证我打开的套接字是否确实支持这些选项。

当我尝试提取辅助数据时,有时会出现问题,使用如下代码,例如,提取带有传输/接收时间戳的 struct scm_timestamping:

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));
    }
}

有时,CMSG_FIRSTHDR(&mhdr) 返回一个空指针并且返回的 mhdr.msg_controllen0,以这种方式我不能提取任何时间戳。 其他时候,在同一台 PC 上并使用相同的 NIC,一切都很好。

所以,我的问题是:通常在哪些情况下返回的 mhdr.msg_controllen 可以是 0?这可能是由于 struct msghdr 的定义有问题吗?还是由于某种内核问题?

最佳答案

很可能是您的控制消息缓冲区不够大,因为它没有对齐,第一个对齐边界以下的部分将无法使用。我不确定设置缓冲区的正确惯用方法是什么(可能使用 malloc 是避免代码的唯一方法,严格来说,由于对齐和有效类型/“别名”而成为 UB "违规),但将 _Alignof(max_align_t)(或 sizeof(max_align_t))添加到缓冲区大小可能是一种解决方案。不过,我想知道公认的“正确”方法是什么。

cmsg(3) man page似乎建议 union 对齐:

      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;

关于c - 在从 Linux 套接字发送/接收数据包后访问辅助数据时,在哪些情况下 msg_controllen 可以为 0?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57186620/

相关文章:

java - 使用java在三个服务器之间进行UDP单播通信

c - unsigned long long int pow

一个fork child能判断它是fork还是vfork吗?

c++ - 使用 sbrk 自定义内存管理

linux - nginx 的 upload_progress 模块不起作用

sockets - 无法在 Windows 8 Metro 风格应用程序中使用 IPAddress 或类似类型

c++ - 如何检测端口是否已在服务器端使用(在 Windows 上的 C++ 中)?

c++ - *(flow + i*n + j) 在 C 中意味着什么?我不知道这个主题叫什么

c - sprintf 自行截断输出字符串

linux - sed中 'Hold space'和 'Pattern space'的概念