I am currently trying to get hardware timestamps via SO_TIMESTAMPING. The interface should support hardware timestamps:
ethtool -T enp175s0
Time stamping parameters for enp175s0:
Capabilities:
hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)
hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)
hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)
PTP Hardware Clock: 2
Hardware Transmit Timestamp Modes:
off (HWTSTAMP_TX_OFF)
on (HWTSTAMP_TX_ON)
Hardware Receive Filter Modes:
none (HWTSTAMP_FILTER_NONE)
all (HWTSTAMP_FILTER_ALL)
Here is a code sample from my udp server script (original source: https://pastebin.com/qd0gspRc):
timestampOn = SOF_TIMESTAMPING_RX_HARDWARE;
printf("timestampOn = %i\n", timestampOn);
r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, ×tampOn, sizeof(timestampOn));
printf("r = %i\n", r);
if (r < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: setsockopt failed: %s\n", strerror(inf->err_no));
return r;
}
printf("Setting socket up with timestamp flags worked.\n");
r = setsockopt(inf->fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
if (r < 0) {
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: setsockopt2 failed: %s\n", strerror(inf->err_no));
return r;
}
const int opt = 1;
r = setsockopt(inf->fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
if (r<0){
inf->err_no = errno;
fprintf(stderr, "setup_udp_server: setsockopt3 failed: %s\n", strerror(inf->err_no));
return r;
}
And here part of the receiver code:
printf("Before receiving: controllen = %i\n", msg.msg_controllen);
recv_len = recvmsg(inf->fd, &msg, 0);
printf("After receiving: controllen = %i\n", msg.msg_controllen);
gettimeofday(&(inf->time_user), NULL);
char ctrl[4096];
memset(ctrl, 0, sizeof(ctrl));
struct cmsghdr *cmsg = (struct cmsghdr *) &ctrl;
struct timespec *ts = NULL;
cmsg = CMSG_FIRSTHDR(&msg);
if(cmsg==NULL){
printf("cmsg = NULL\n");
}
else{
printf("cmsg level = %i, cmsg type = %i\n", cmsg->cmsg_level, cmsg->cmsg_type);
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
printf("cmsg level = %i, cmsg type = %i\n", cmsg->cmsg_level, cmsg->cmsg_type);
printf("SOL_SOCKET = %i, SO_TIMESTAMPING = %i\n" ,SOL_SOCKET, SO_TIMESTAMPING);
printf("IPPROTO_IP = %i, IP_PKTINFO = %i\n", IPPROTO_IP, IP_PKTINFO);
if (SOL_SOCKET == cmsg->cmsg_level && SO_TIMESTAMPING == cmsg->cmsg_type) {
ts = (struct timespec *) CMSG_DATA(cmsg); //maybe it's not timespec, but timeval?
inf->time_kernel.tv_sec=ts[0].tv_sec;
inf->time_kernel.tv_usec=ts[0].tv_nsec/1000;
printf("SW TIMESTAMP %ld.%09ld\n", (long)ts[0].tv_sec, (long)ts[0].tv_nsec);
printf("deprecated TIMESTAMP %ld.%09ld\n", (long)ts[1].tv_sec, (long)ts[1].tv_nsec);
printf("HW TIMESTAMP %ld.%09ld\n", (long)ts[2].tv_sec, (long)ts[2].tv_nsec);
}
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO){
in_pktinfo = *(struct in_pktinfo*)CMSG_DATA(cmsg);
printf("PKTINFO extracted.\n");
char interface[255];
char addr_1[255];
char addr_2[255];
if_indextoname(in_pktinfo.ipi_ifindex,interface);
printf("Interface = %s\n", interface);
inet_ntop(AF_INET, &in_pktinfo.ipi_spec_dst, addr_1, INET_ADDRSTRLEN);
printf("IPI_SPEC_DST = %s\n", addr_1);
inet_ntop(AF_INET, &in_pktinfo.ipi_addr, addr_2, INET_ADDRSTRLEN);
printf("IPI_ADDR = %s\n", addr_2);
}
}
The output is
Before receiving: controllen = 4096
After receiving: controllen = 32
cmsg level = 0, cmsg type = 8
SOL_SOCKET = 1, TIMESTAMP_FLAG = 37
IPPROTO_IP = 0, IP_PKTINFO = 8
PKTINFO extracted.
Interface = enp175s0
IPI_SPEC_DST = 10.64.0.145
IPI_ADDR = 10.64.0.145
level=0, type=8, len=28
End messages
indicating that the ancillary data with the IPPROTO_IP
part have been generated, but the SO_TIMESTAMPING
part is missing. If I use SO_TIMESTAMPNS
instead, it works, but in this case the timestamps are not generated by hardware anymore.
Any ideas how to overcome this issue?