Linux TCP套接字的时间戳功能

14

我正在进行一个项目,旨在从NIC获取TCP套接字的接收和传输时间戳,如Linux时间戳文档中所述。但是所有文档和测试代码都是针对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)

我使用ioctl(sockfd, SIOCSHWTSTAMP, & net_device);bind()之后为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(),检查是否 fdrfds 中,然后使用以下选项调用 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接收硬件时间戳。

2个回答

7

我能够在最近的内核版本(4.18)中使用SO_TIMESTAMPING与TCP套接字一起使用,这适用于TX和RX时间戳。尽管我仍在努力,但如果您愿意,我可以尝试编写一个最小的概念性证明。


如果您读取了分段接收的数据,应该使用哪个时间戳?或者有没有一种方法可以显示分段视图? - Joseph Garvin

3

Linux时间戳不支持接收硬件或软件时间戳的TCP。 Linux时间戳的文档仅涉及“数据包”。这指的是UDP,用于在NIC中实现PTP硬件时钟同步的实现,请查看PTP守护程序和Linux PTP代码以获取更多理解。Linux 3.18仅支持传输中的时间戳。因此,基本上您无法在接收器中实现Linux时间戳的TCP。


有没有办法获取TCP的时间戳? - David 天宇 Wong
值得添加的是,一些网络接口卡(NICs)和 Linux 驱动程序支持 UDP 数据包的通用时间戳,不仅仅是为了 PTP。 - Erik Alapää
我知道这很老了,但是我认为libpcap支持TCP硬件时间戳。 - notAChance
截至今天,即2017年4月18日,来自Solarflare(网络卡制造商)的支持人员报告说,Linux内核不支持TCP硬件时间戳。您可以从卸载的网络卡驱动程序中获取它,例如SFN8522-PLUS这样的卡,但该卡也没有完全成熟 :-(。 - Mike S
1
引用自https://www.kernel.org/doc/Documentation/networking/timestamping.txt: “SO_TIMESTAMPING 在接收、传输或两者上生成时间戳。支持多个时间戳源,包括硬件。支持为流套接字生成时间戳。”你确定Linux不支持TCP时间戳吗?尽管我仍在尝试获取TCP的时间戳,但似乎并非如此。 - FaceBro
1
我的问题是,即使它被支持了,这意味着什么?TCP是一个流,通常流的分段是不可见的,所以你得到的时间戳对应于...? - Joseph Garvin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接