Linux TCP 套接字的时间戳
Linux timestamping for TCP sockets
我正在一个项目中工作,如文档 Linux timestamping 中所述,从用于 TCP 套接字的 NIC 获取接收和传输时间戳。但是所有文档和测试编码都是针对 UDP 套接字完成的。但是我得到了 NIC 的传输时间戳,但没有得到接收 TCP 数据包的时间戳。
我的界面支持以下时间戳
Time stamping parameters for enp4s0:
Capabilities:
hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)
software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)
hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)
software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)
software-system-clock (SOF_TIMESTAMPING_SOFTWARE)
hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)
PTP Hardware Clock: 3
Hardware Transmit Timestamp Modes:
off (HWTSTAMP_TX_OFF)
on (HWTSTAMP_TX_ON)
Hardware Receive Filter Modes:
none (HWTSTAMP_FILTER_NONE)
all (HWTSTAMP_FILTER_ALL)
我在 bind()
之后使用 ioctl(sockfd, SIOCSHWTSTAMP, &net_device);
和
为 NIC 启用时间戳
memset(&net_device, 0, sizeof(net_device));
strncpy(net_device.ifr_name, interface_name, sizeof(net_device.ifr_name));
net_device.ifr_data = (void *)&tstconfig;
memset(&tstconfig, 0, sizeof(tstconfig));
tstconfig.tx_type = HWTSTAMP_TX_OFF;
tstconfig.rx_filter = HWTSTAMP_FILTER_ALL;
然后通过setsockopt()
在网卡中启用时间戳
int opt= 0;
opt |= SOF_TIMESTAMPING_RX_HARDWARE;
opt |= SOF_TIMESTAMPING_RAW_HARDWARE;
if (setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMPING,
(char *)&opt, sizeof(opt))) {
error(1, 0, "setsockopt timestamping");
bail("setsockopt SO_TIMESTAMPING");
}
在 listen() 和 accept() 之后,我执行 select()
,并检查 fd
是否为 rfds
然后使用以下选项调用 recvmsg()
int rc;
struct iovec vec[1];
struct msghdr msg;
char data[8192];
struct cmsghdr *cmsg;
union {
struct cmsghdr cm;
char control[256];
} cmsg_un;
vec[0].iov_base = data;
vec[0].iov_len = sizeof(data);
memset(&msg, 0, sizeof(msg));
memset(&from_addr, 0, sizeof(from_addr));
memset(&cmsg_un, 0, sizeof(cmsg_un));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = vec;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_un.control;
msg.msg_controllen = sizeof(cmsg_un.control);
rc = recvmsg(flow->fd, &msg, 0);
printf("tried reading %d bytes, got %d", bytes, rc);
if (msg.msg_flags & MSG_TRUNC) {
printf("received truncated message\n");
return 0;
}
if (msg.msg_flags & MSG_CTRUNC) {
printf("received truncated ancillary data\n");
return 0;
}
if (msg.msg_controllen <= 0) {
printf("`received short ancillary data (%ld/%ld)`\n",
(long)msg.msg_controllen, (long)sizeof(cmsg_un.control));
return 0;
}
但我总是收到以下消息,
received short ancillary data (0/256)
我没有从 recvmsg()
获取辅助数据,我想知道 linux 是否支持 NIC 的 TCP 接收硬件时间戳。
Linux 时间戳不支持 TCP 的接收硬件或软件时间戳。 linux 时间戳的文档仅在 "packets" 方面提及。这里指的是UDP,用于实现网卡中PTP硬件时钟的同步,看PTP daemon和linuxptp的代码更了解。 Linux 3.18 仅支持传输中的时间戳。所以基本上你无法在接收器中为 TCP 实现 linux 时间戳。
我能够使用 SO_TIMESTAMPING 使用最新内核版本 (4.18) 的 TCP 套接字。这适用于 TX 和 RX 时间戳。我仍在努力,但如果你愿意,我可以尝试写一个最小的概念证明。
我正在一个项目中工作,如文档 Linux timestamping 中所述,从用于 TCP 套接字的 NIC 获取接收和传输时间戳。但是所有文档和测试编码都是针对 UDP 套接字完成的。但是我得到了 NIC 的传输时间戳,但没有得到接收 TCP 数据包的时间戳。
我的界面支持以下时间戳
Time stamping parameters for enp4s0:
Capabilities:
hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)
software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)
hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)
software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)
software-system-clock (SOF_TIMESTAMPING_SOFTWARE)
hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)
PTP Hardware Clock: 3
Hardware Transmit Timestamp Modes:
off (HWTSTAMP_TX_OFF)
on (HWTSTAMP_TX_ON)
Hardware Receive Filter Modes:
none (HWTSTAMP_FILTER_NONE)
all (HWTSTAMP_FILTER_ALL)
我在 bind()
之后使用 ioctl(sockfd, SIOCSHWTSTAMP, &net_device);
和
memset(&net_device, 0, sizeof(net_device));
strncpy(net_device.ifr_name, interface_name, sizeof(net_device.ifr_name));
net_device.ifr_data = (void *)&tstconfig;
memset(&tstconfig, 0, sizeof(tstconfig));
tstconfig.tx_type = HWTSTAMP_TX_OFF;
tstconfig.rx_filter = HWTSTAMP_FILTER_ALL;
然后通过setsockopt()
int opt= 0;
opt |= SOF_TIMESTAMPING_RX_HARDWARE;
opt |= SOF_TIMESTAMPING_RAW_HARDWARE;
if (setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMPING,
(char *)&opt, sizeof(opt))) {
error(1, 0, "setsockopt timestamping");
bail("setsockopt SO_TIMESTAMPING");
}
在 listen() 和 accept() 之后,我执行 select()
,并检查 fd
是否为 rfds
然后使用以下选项调用 recvmsg()
int rc;
struct iovec vec[1];
struct msghdr msg;
char data[8192];
struct cmsghdr *cmsg;
union {
struct cmsghdr cm;
char control[256];
} cmsg_un;
vec[0].iov_base = data;
vec[0].iov_len = sizeof(data);
memset(&msg, 0, sizeof(msg));
memset(&from_addr, 0, sizeof(from_addr));
memset(&cmsg_un, 0, sizeof(cmsg_un));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = vec;
msg.msg_iovlen = 1;
msg.msg_control = cmsg_un.control;
msg.msg_controllen = sizeof(cmsg_un.control);
rc = recvmsg(flow->fd, &msg, 0);
printf("tried reading %d bytes, got %d", bytes, rc);
if (msg.msg_flags & MSG_TRUNC) {
printf("received truncated message\n");
return 0;
}
if (msg.msg_flags & MSG_CTRUNC) {
printf("received truncated ancillary data\n");
return 0;
}
if (msg.msg_controllen <= 0) {
printf("`received short ancillary data (%ld/%ld)`\n",
(long)msg.msg_controllen, (long)sizeof(cmsg_un.control));
return 0;
}
但我总是收到以下消息,
received short ancillary data (0/256)
我没有从 recvmsg()
获取辅助数据,我想知道 linux 是否支持 NIC 的 TCP 接收硬件时间戳。
Linux 时间戳不支持 TCP 的接收硬件或软件时间戳。 linux 时间戳的文档仅在 "packets" 方面提及。这里指的是UDP,用于实现网卡中PTP硬件时钟的同步,看PTP daemon和linuxptp的代码更了解。 Linux 3.18 仅支持传输中的时间戳。所以基本上你无法在接收器中为 TCP 实现 linux 时间戳。
我能够使用 SO_TIMESTAMPING 使用最新内核版本 (4.18) 的 TCP 套接字。这适用于 TX 和 RX 时间戳。我仍在努力,但如果你愿意,我可以尝试写一个最小的概念证明。