时间戳输出数据包

我试图得到传出数据包的准确时间戳(使用原始套接字发送)。 根据Linux/Documentation/networking/timestamping.txt ,“对于发送时间标记,发出的数据包会循环回到套接字的错误队列中,并附上发送时间标记,可以用recvmsg(flags = MSG_ERRQUEUE)接收。 ”。

不幸的是, recvmsg总是在原始套接字上调用时返回-1 (使用socket(PF_INET, SOCK_RAW, IPPROTO_RAW)创build, SO_TIMESTAMP使用setsockopt设置为1 )。 我究竟做错了什么? 是否有一个更好的方式获得一个传出数据包的准确时间戳?

附录(信息):

我也尝试从通过UDP套接字发送的数据包(源代码如下)获取时间戳,并且recvmsg返回-1 :错误是“资源暂时不可用”( EAGAIN )。

附录(源代码):

 #include <arpa/inet.h> #include <linux/net_tstamp.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> void die(char* s) { perror(s); exit(1); } int main(int argc, char* argv[]) { char* destination_ip = "10.0.0.1"; int destination_port = 1234; int sock; if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { die("socket()"); } int timestamp_flags = SOF_TIMESTAMPING_TX_SOFTWARE; if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &timestamp_flags, sizeof(timestamp_flags)) < 0) { die("setsockopt()"); } struct sockaddr_in si_server; memset(&si_server, 0, sizeof(si_server)); si_server.sin_family = AF_INET; si_server.sin_port = htons(destination_port); if (inet_aton(destination_ip, &si_server.sin_addr) == 0) { die("inet_aton()"); } const int buffer_len = 256; char buffer[buffer_len]; const int n_packets = 10; for (int i = 0; i < n_packets; ++i) { sprintf(buffer, "Packet %d", i); if (sendto(sock, buffer, buffer_len, 0, (const sockaddr*) &si_server, sizeof(si_server)) < 0) { die("sendto()"); } // Obtain the sent packet timestamp. char data[256]; struct msghdr msg; struct iovec entry; struct sockaddr_in from_addr; struct { struct cmsghdr cm; char control[512]; } control; int res; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &entry; msg.msg_iovlen = 1; entry.iov_base = data; entry.iov_len = sizeof(data); msg.msg_name = (caddr_t)&from_addr; msg.msg_namelen = sizeof(from_addr); msg.msg_control = &control; msg.msg_controllen = sizeof(control); if (recvmsg(sock, &msg, MSG_ERRQUEUE) < 0) { die("recvmsg()"); } } return 0; } 

Solutions Collecting From Web of "时间戳输出数据包"

查看Linux内核源代码,我发现负责将包含包的时间戳的消息放入错误队列的函数是skb_tx_timestamp 。 这个功能应该是由NIC驱动程序调用的,不幸的是, e1000驱动程序不会调用它(硬件时间戳有类似的功能,但这显然取决于支持它的NIC驱动程序)。

根据去年9月的NetDev讨论 ,“没有驱动程序调用skb_tx_timestamp()”和“你需要调整你的网卡驱动程序来使用这个TX时间戳”。 在e1000_xmit_frame上添加一个对skb_tx_timestampe1000_xmit_frame的调用后,我能够获得传出数据包的时间戳(通过UDP套接字)。 我无法获得RAW套接字EAGAIN出数据包的时间戳,但(我仍然得到EAGAIN )。

由于我们看不到您的代码,因此很难知道您做错了什么。

但是:文档说SO_TIMESTAMP用于传入数据包,而SO_TIMESTAMPING用于传出数据包。

内核文档包含一个完整的例子,你可以使用它作为基础 – 虽然它使用UDP,但你应该能够调整到使用RAW套接字。 请参阅linux内核Documentation / networking / timestamping / timestamping.c

编辑:它似乎传递时间戳不普遍支持,见例如这里 。 即使在今天,只有少数的驱动程序实现了软件支持,少数驱动程序也具有硬件支持。

sock_tx_timestamp仅针对当前内核代码中的SOCK_DGRAM套接字调用。

顺便说一下,文档Documentation / networking / timestamping / timestamping.c是不是很准确。

SO_TIMESTAMP / SO_TIMESTAMPNS / SO_TIMESTAMPING / SIOCGSTAMP / SIOCGSTAMPNS类似。 任何人都将使应用程序获得收到的数据包的时间戳。

使用SOF_TIMESTAMPING_TX_SOFTWARE,上述任何一个标志都将在应用程序中为MSG_ERRQUEUE提供一个CMSG,指示已发送数据包的时间戳。

但SOF_TIMESTAMPING_RX_SOFTWARE完全没用。 它甚至不能用来禁止报告接收到的数据包的时间戳。

我想,你必须提及时间戳标志

 int timestamp_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; timestamp_flags |= SOF_TIMESTAMPING_SOFTWARE; 

而且在从msg队列错误中获取数据之前,还必须检查所有系统调用的strerror。 这将帮助您摆脱EAGAIN消息。