IP_PKTINFO套接字选项不起作用

现在我已经在这个问题上磕头了几个星期了,终于提出了我无法弄清楚的事实。 我也一直在与我的团队的networking工程师合作无济于事。 我的问题如下:

我正在开发一个应用程序,在多个vlans上进行非常简单的UDP组连接(每个vlan都公开为自己的虚拟接口,在这种情况下,NIC是一个SolarFlare,如果相关的话)。 所有这些连接都发生在一个单一的套接字上(消息根据有效载荷序列号去重)。 在做IP_ADD_MEMBERSHIP之前,我要设置这样的套接字选项:

setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &yes, sizeof yes) setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) setsockopt(sock, IPPROTO_IP, PACKET_AUXDATA, &yes, sizeof(yes)) 

我需要通过IP_PKTINFO获取接口索引或通过PACKET_AUXDATA获取vlan id,以便收集下游统计信息。 现在,一切都初始化没有错误,我能够处理UDP有效载荷没有问题。 遇到麻烦的地方是当我尝试访问上面请求的辅助/控制消息时,如简单的debugging日志所示:

 for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { printf("Control Message: cmsg_level: %d, cmsg_type %d\n", cmsgptr->cmsg_level, cmsgptr->cmsg_type); } 

对于每个收到的数据包,这只输出:

 Control Message: cmsg_level: 1, cmsg_type 29 

作为参考,SOL_SOCKET = 1和SO_TIMESTAMP = 29。 所以,虽然我正在请求3种不同的控制消息types,但只有时间戳正在被填充。 此行为与我是在单个接口上join单个UDP组还是在多个接口上join多个组无关。

一种解决scheme是重写应用程序,将每个接口放在自己的套接字上,然后将所有东西都集中到一个队列中,但根据我的经验,上下文切换会导致应用程序的性能下降。 根据手册页IP(7)自Linux内核2.2以来IP_PKTINFO已经可用。 我正在运行使用内核3.13.0-24-generic的Ubuntu 14.04.4。

任何帮助,见解或方向将不胜感激!

在黑暗中猜测

1)每次成功调用setsockopt之后,需要将“ yes重置为1。 文件暗示这不是必要的,但这是我会做的。

 int yes = 1; setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &yes, sizeof(yes)); yes = 1; setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)); yes = 1; setsockopt(sock, IPPROTO_IP, PACKET_AUXDATA, &yes, sizeof(yes)); 

2)你检查setsockopt的返回值,看看这些调用是否正确成功。 目前还不清楚你是否验证过他们返回“0”成功或错误“-1”。 您应该打印每个呼叫的返回码。

3)你没有显示你的recvmsg代码,这是你大概用来获得额外的信息。 但可能的struct msghdr没有正确初始化。 特别是你的缓冲区大到足以获得所有的控制数据? 以下是我在代码中的做法:

 struct iovec vec; ssize_t ret; const size_t CONTROL_DATA_SIZE = 1000; // THIS NEEDS TO BE BIG ENOUGH. char controldata[CONTROL_DATA_SIZE]; struct msghdr hdr = {}; sockaddr_storage addrRemote = {}; vec.iov_base = buf; vec.iov_len = len; hdr.msg_name = &addrRemote; hdr.msg_namelen = sizeof(addrRemote); hdr.msg_iov = &vec; hdr.msg_iovlen = 1; hdr.msg_control = controldata; hdr.msg_controllen = CONTROL_DATA_SIZE; ret = ::recvmsg(sockfd, &hdr, flags);