- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试找到一种方法,在 Linux 下并使用 C 以编程方式检查给定 NIC 是否支持软件传输时间戳 ( SOF_TIMESTAMPING_TX_SOFTWARE
),以便恢复为其他类型的时间戳(或完全禁用它们) ) 如果它们不受支持。
特别是,我的目标是在调用 ioctl(SIOCSHWTSTAMP)
时检查它们是否受支持,就像硬件时间戳一样。并检查它的返回值(可以找到更新的文档 here )。
ethtool -T <interface name>
已经提供了这些信息,但我认为调用 system()
不是个好主意。或 popen()
, 作为 ethtool
可能没有安装在系统上,我绝对不想把它作为运行我的程序的先决条件。
在进行一些试验时,我使用了来自 this question 的代码改编版:
#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>
#include <linux/errqueue.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <unistd.h>
#include <time.h>
#include <poll.h>
#include <linux/if.h>
#define RAW_SOCKET 0 // Set to 0 to use an UDP socket, set to 1 to use raw socket
#define NUM_TESTS 2
#if RAW_SOCKET
#include <linux/if_packet.h>
#include <net/ethernet.h>
#endif
void die(char* s)
{
perror(s);
exit(1);
}
// Wait for data to be available on the socket error queue, as detailed in https://www.kernel.org/doc/Documentation/networking/timestamping.txt
int pollErrqueueWait(int sock,uint64_t timeout_ms) {
struct pollfd errqueueMon;
int poll_retval;
errqueueMon.fd=sock;
errqueueMon.revents=0;
errqueueMon.events=0;
while((poll_retval=poll(&errqueueMon,1,timeout_ms))>0 && errqueueMon.revents!=POLLERR);
return poll_retval;
}
int run_test(int argc, char* argv[], int hw_stamps, int sock, void *si_server_ptr)
{
#if RAW_SOCKET
struct sockaddr_ll si_server=*(struct sockaddr_ll *) si_server_ptr;
#else
struct sockaddr_in si_server=*(struct sockaddr_in *) si_server_ptr;
#endif
fprintf(stdout,"Test started.\n");
int flags;
if(hw_stamps) {
struct ifreq hwtstamp;
struct hwtstamp_config hwconfig;
// Set hardware timestamping
memset(&hwtstamp,0,sizeof(hwtstamp));
memset(&hwconfig,0,sizeof(hwconfig));
// Set ifr_name and ifr_data (see: man7.org/linux/man-pages/man7/netdevice.7.html)
strncpy(hwtstamp.ifr_name,argv[1],sizeof(hwtstamp.ifr_name));
hwtstamp.ifr_data=(void *)&hwconfig;
hwconfig.tx_type=HWTSTAMP_TX_ON;
hwconfig.rx_filter=HWTSTAMP_FILTER_ALL;
// Issue request to the driver
if (ioctl(sock,SIOCSHWTSTAMP,&hwtstamp)<0) {
die("ioctl()");
}
flags=SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE;
} else {
flags=SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE;
}
if(setsockopt(sock,SOL_SOCKET,SO_TIMESTAMPING,&flags,sizeof(flags))<0) {
die("setsockopt()");
}
const int buffer_len = 256;
char buffer[buffer_len];
// Send 10 packets
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, (struct sockaddr*) &si_server, sizeof(si_server)) < 0) {
die("sendto()");
}
fprintf(stdout,"Sent packet number %d/%d\n",i,n_packets);
fflush(stdout);
// Obtain the sent packet timestamp.
char data[256];
struct msghdr msg;
struct iovec entry;
char ctrlBuf[CMSG_SPACE(sizeof(struct scm_timestamping))];
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 = NULL;
msg.msg_namelen = 0;
msg.msg_control = &ctrlBuf;
msg.msg_controllen = sizeof(ctrlBuf);
// Wait for data to be available on the error queue
pollErrqueueWait(sock,-1); // -1 = no timeout is set
if (recvmsg(sock, &msg, MSG_ERRQUEUE) < 0) {
die("recvmsg()");
}
// Extract and print ancillary data (SW or HW tx timestamps)
struct cmsghdr *cmsg = NULL;
struct scm_timestamping hw_ts;
for(cmsg=CMSG_FIRSTHDR(&msg);cmsg!=NULL;cmsg=CMSG_NXTHDR(&msg, cmsg)) {
if(cmsg->cmsg_level==SOL_SOCKET && cmsg->cmsg_type==SO_TIMESTAMPING) {
hw_ts=*((struct scm_timestamping *)CMSG_DATA(cmsg));
fprintf(stdout,"HW: %lu s, %lu ns\n",hw_ts.ts[2].tv_sec,hw_ts.ts[2].tv_nsec);
fprintf(stdout,"ts[1] - ???: %lu s, %lu ns\n",hw_ts.ts[1].tv_sec,hw_ts.ts[1].tv_nsec);
fprintf(stdout,"SW: %lu s, %lu ns\n",hw_ts.ts[0].tv_sec,hw_ts.ts[0].tv_nsec);
}
}
// Wait 1s before sending next packet
sleep(1);
}
return 0;
}
int main(int argc, char* argv[]) {
int sock;
char* destination_ip = "192.168.1.211";
int destination_port = 1234;
struct in_addr sourceIP;
fprintf(stdout,"Program started.\n");
if(argc!=2) {
fprintf(stderr,"Error. You should specify the interface name.\n");
exit(1);
}
// Create socket
#if RAW_SOCKET
if ((sock = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL))) < 0) {
die("RAW socket()");
}
#else
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
die("UDP socket()");
}
#endif
struct ifreq ifindexreq;
#if RAW_SOCKET
struct sockaddr_ll si_server;
int ifindex=-1;
// Get interface index
strncpy(ifindexreq.ifr_name,argv[1],IFNAMSIZ);
if(ioctl(sock,SIOCGIFINDEX,&ifindexreq)!=-1) {
ifindex=ifindexreq.ifr_ifindex;
} else {
die("SIOCGIFINDEX ioctl()");
}
memset(&si_server, 0, sizeof(si_server));
si_server.sll_ifindex=ifindex;
si_server.sll_family=AF_PACKET;
si_server.sll_protocol=htons(ETH_P_ALL);
#else
struct sockaddr_in si_server;
// Get source IP address
strncpy(ifindexreq.ifr_name,argv[1],IFNAMSIZ);
ifindexreq.ifr_addr.sa_family = AF_INET;
if(ioctl(sock,SIOCGIFADDR,&ifindexreq)!=-1) {
sourceIP=((struct sockaddr_in*)&ifindexreq.ifr_addr)->sin_addr;
} else {
die("SIOCGIFADDR ioctl()");
}
bzero(&si_server,sizeof(si_server));
si_server.sin_family = AF_INET;
si_server.sin_port = htons(destination_port);
si_server.sin_addr.s_addr = sourceIP.s_addr;
fprintf(stdout,"source IP: %s\n",inet_ntoa(sourceIP));
#endif
// bind() to interface
if(bind(sock,(struct sockaddr *) &si_server,sizeof(si_server))<0) {
die("bind()");
}
#if !RAW_SOCKET
// Set destination IP (re-using si_server)
if (inet_aton(destination_ip, &si_server.sin_addr) == 0) {
die("inet_aton()");
}
#endif
for(int i=0;i<NUM_TESTS;i++) {
fprintf(stdout,"Iteration: %d - HW_STAMPS? %d\n",i,i%2);
run_test(argc,argv,i%2,sock,(void *)&si_server);
}
close(sock);
return 0;
}
此代码将发送 10 个请求软件传输时间戳的数据包,然后它会尝试发送其他 10 个数据包,但请求硬件传输时间戳,依此类推。
它以应该发送数据包的接口(interface)名称作为参数。我注意到,根据 kernel timestamping documentation,当支持传输硬件/软件时间戳时,一切都按预期工作。 ,如 enp0s31f6
(以太网)接口(interface)案例:
$ sudo ./test enp0s31f6
Program started.
source IP: 192.168.1.210
Iteration: 0 - HW_STAMPS? 0
Test started.
Sent packet number 0/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878575 s, 690256891 ns
Sent packet number 1/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878576 s, 690468816 ns
Sent packet number 2/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878577 s, 691003245 ns
Sent packet number 3/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878578 s, 691365791 ns
Sent packet number 4/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878579 s, 691940147 ns
Sent packet number 5/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878580 s, 692198712 ns
Sent packet number 6/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878581 s, 692543005 ns
Sent packet number 7/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878582 s, 692856348 ns
Sent packet number 8/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878583 s, 693098097 ns
Sent packet number 9/10
HW: 0 s, 0 ns
ts[1] - ???: 0 s, 0 ns
SW: 1563878584 s, 693612477 ns
Iteration: 1 - HW_STAMPS? 1
Test started.
Sent packet number 0/10
HW: 1563878585 s, 717541747 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 1/10
HW: 1563878586 s, 718023872 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 2/10
HW: 1563878587 s, 718505122 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 3/10
HW: 1563878588 s, 719091997 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 4/10
HW: 1563878589 s, 719689747 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 5/10
HW: 1563878590 s, 720231247 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 6/10
HW: 1563878591 s, 720462747 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 7/10
HW: 1563878592 s, 721012872 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 8/10
HW: 1563878593 s, 721272372 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
Sent packet number 9/10
HW: 1563878594 s, 721588497 ns
ts[1] - ???: 0 s, 0 ns
SW: 0 s, 0 ns
相反,如果我尝试通过无线接口(interface)启动示例程序,不支持任何类型的传输时间戳,如 ethtool
所报告的那样:
$ ethtool -T wlp1s0
Time stamping parameters for wlp1s0:
Capabilities:
software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)
software-system-clock (SOF_TIMESTAMPING_SOFTWARE)
PTP Hardware Clock: none
Hardware Transmit Timestamp Modes: none
Hardware Receive Filter Modes: none
关于软件传输时间戳,我从来没有收到任何消息循环回错误队列,导致无限期等待 -1
指定为 poll
超时,或 EAGAIN
如果指定了超时则出错(并且超时,设置时,所有时间):
sudo ./test wlp1s0
Program started.
source IP: 172.22.116.105
Iteration: 0 - HW_STAMPS? 0
Test started.
Sent packet number 0/10
.....<stops here>.....
使用 UDP 套接字和使用原始套接字(通过将 #define RAW_SOCKET
设置为 1
或 0
)时,结果是相同的。
为了避免等待永远不会到来的环回消息(或等待超时到期),有没有一种方法可以让我以编程方式检查 SOF_TIMESTAMPING_TX_SOFTWARE
是否存在?在给定接口(interface)上支持并最终禁用我程序中的整个机制,然后再尝试检索无法检索的传输时间戳?
非常感谢您。
最佳答案
您应该使用与 ethtool
相同的接口(interface)。有一个名为 SIOCETHTOOL
的特定 ioctl,它从驱动程序级别检索有关时间戳功能的信息。这是一个简短的示例(为简洁起见,缺少错误处理等):
// Specify the ethtool parameter family (timestamping)
struct ethtool_ts_info tsi = {.cmd = ETHTOOL_GET_TS_INFO};
// Specify interface to use (eth1 in this example) and pass data buffer
struct ifreq ifr = {.ifr_name = "eth1", .ifr_data = (void*)&tsi};
// Create a socket for the ioctl command
int fd = socket(AF_INET, SOCK_DGRAM, 0);
// Perform the ioctl
ioctl(fd, SIOCETHTOOL, &ifr);
// and analyze the results
if (tsi.so_timestamping & SOF_TIMESTAMPING_TX_HARDWARE)
printf("%s supports hardware tx timestamps\n", ifr.ifr_name);
if (tsi.so_timestamping & SOF_TIMESTAMPING_TX_SOFTWARE)
printf("%s supports software tx timestamps\n", ifr.ifr_name);
RX 时间戳相同。这样您应该能够确定是否支持时间戳。
关于c - 在 Linux 下,如何以编程方式检查给定 NIC 是否支持传输时间戳?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57164125/
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
Linux 管道可以缓冲多少数据?这是可配置的吗? 如果管道的两端在同一个进程中,但线程不同,这会有什么不同吗? 请注意:这个“同一个进程,两个线程”的问题是理论上的边栏,真正的问题是关于缓冲的。 最
我找到了here [最后一页] 一种有趣的通过 Linux 启动 Linux 的方法。不幸的是,它只是被提及,我在网上找不到任何有用的链接。那么有人听说过一种避免引导加载程序而使用 Linux 的方法
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我试图了解 ld-linux.so 如何在 Linux 上解析对版本化符号的引用。我有以下文件: 测试.c: void f(); int main() { f(); } a.c 和 b.c:
与 RetroPie 的工作原理类似,我可以使用 Linux 应用程序作为我的桌面环境吗?我实际上并不需要像实际桌面和安装应用程序这样的东西。我只需要一种干净简单的方法来在 RaspberryPi 上
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 10 年前。 Improve thi
有什么方法可以覆盖现有的源代码,我应该用 PyQt、PyGTK、Java 等从头开始构建吗? 最佳答案 如果您指的是软件本身而不是它所连接的存储库,那么自定义应用程序的方法就是 fork 项目。据我所
我的情况是:我在一个磁盘上安装了两个 linux。我将第一个安装在/dev/sda1 中,然后在/dev/sda2 中安装第二个然后我运行第一个系统,我写了一个脚本来在第一个系统运行时更新它。
我在 i2c-0 总线上使用地址为 0x3f 的系统监视器设备。该设备在设备树中配置有 pmbus 驱动程序。 问题是,加载 linux 内核时,这个“Sysmon”设备没有供电。因此,当我在总线 0
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve thi
我正试图在 linux 模块中分配一大块内存,而 kalloc 做不到。 我知道唯一的方法是使用 alloc_bootmem(unsigned long size) 但我只能从 linux 内核而不是
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我有 .sh 文件来运行应用程序。在该文件中,我想动态设置服务器名称,而不是每次都配置。 我尝试了以下方法,它在 CentOS 中运行良好。 nohup /voip/java/jdk1.8.0_71/
我是在 Linux 上开发嵌入式 C++ 程序的新手。我有我的 Debian 操作系统,我在其中开发和编译了我的 C++ 项目(一个简单的控制台进程)。 我想将我的应用程序放到另一个 Debian 操
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 4 年前。 Improve this ques
我使用4.19.78版本的稳定内核,我想找到带有企鹅二进制数据的C数组。系统启动时显示。我需要在哪里搜索该内容? 我在 include/linux/linux_logo.h 文件中只找到了一些 Log
我知道可以使用 gdb 的服务器模式远程调试代码,我知道可以调试针对另一种架构交叉编译的代码,但是是否可以更进一步,从远程调试 Linux 应用程序OS X 使用 gdbserver? 最佳答案 当然
是否有任何可能的方法来运行在另一个 Linux 上编译的二进制文件?我知道当然最简单的是在另一台机器上重建它,但假设我们唯一能得到的是一个二进制文件,那么这可能与否? (我知道这可能并不容易,但我只是
我是一名优秀的程序员,十分优秀!