1

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, &timestampOn, 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?

  • According to https://www.kernel.org/doc/Documentation/networking/timestamping.txt the value passed to `setsockopt` should be a combination of flags. Try `SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG` – Ben Voigt Dec 03 '20 at 16:20
  • Also in that document, pay attention to the section starting with the sentence **Hardware time stamping must also be initialized for each device driver that is expected to do hardware time stamping.** – Ben Voigt Dec 03 '20 at 16:21
  • Thank you for your help! Actually I usually use 'SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE' . But even after adding 'SOF_TIMESTAMPING_OPT_CMSG', the controllen still remains 32 and no timestamp is generated. How would I initialize the hardware stamping for each device driver? – WhyNoPython Dec 04 '20 at 08:16
  • I actually found an answer here: https://stackoverflow.com/questions/47313383/linux-udp-datagrams-and-kernel-timestamps-lots-of-examples-and-stackoversflow The answer to that question worked for me, too. I didn't know about the "hwstampctl" stuff either. – WhyNoPython Dec 04 '20 at 10:59
  • It seems like your question may actually be a duplicate of that existing (and answered) one? If we close yours as a duplicate it will become a permanent signpost -- anyone whose search finds yours will be directed to the existing question and its answers. – Ben Voigt Dec 04 '20 at 18:35
  • You will find the answer to "How would I initialize the hardware stamping for each device driver?" (without using a separate tool such as `hwstamp_ctl`) when you read the non-bold portion of my comment. – Ben Voigt Dec 04 '20 at 18:42
  • @BenVoigt alright then close the question as a duplicate. There is nothing to add to the answers there. – WhyNoPython Dec 07 '20 at 12:17

0 Answers0