selftests: move timestamping selftests to net folder
authorJian Yang <jianyang@google.com>
Wed, 25 Mar 2020 20:32:07 +0000 (13:32 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Mar 2020 04:48:30 +0000 (21:48 -0700)
For historical reasons, there are several timestamping selftest targets
in selftests/networking/timestamping. Move them to the standard
directory for networking tests: selftests/net.

Signed-off-by: Jian Yang <jianyang@google.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
17 files changed:
tools/testing/selftests/Makefile
tools/testing/selftests/net/.gitignore
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/config
tools/testing/selftests/net/hwtstamp_config.c [new file with mode: 0644]
tools/testing/selftests/net/rxtimestamp.c [new file with mode: 0644]
tools/testing/selftests/net/timestamping.c [new file with mode: 0644]
tools/testing/selftests/net/txtimestamp.c [new file with mode: 0644]
tools/testing/selftests/net/txtimestamp.sh [new file with mode: 0755]
tools/testing/selftests/networking/timestamping/.gitignore [deleted file]
tools/testing/selftests/networking/timestamping/Makefile [deleted file]
tools/testing/selftests/networking/timestamping/config [deleted file]
tools/testing/selftests/networking/timestamping/hwtstamp_config.c [deleted file]
tools/testing/selftests/networking/timestamping/rxtimestamp.c [deleted file]
tools/testing/selftests/networking/timestamping/timestamping.c [deleted file]
tools/testing/selftests/networking/timestamping/txtimestamp.c [deleted file]
tools/testing/selftests/networking/timestamping/txtimestamp.sh [deleted file]

index b93fa645ee5472ef553b0b7b55942b6d49f399f2..077818d0197f38f76281f58106809a18915db37a 100644 (file)
@@ -36,7 +36,6 @@ TARGETS += net
 TARGETS += net/forwarding
 TARGETS += net/mptcp
 TARGETS += netfilter
-TARGETS += networking/timestamping
 TARGETS += nsfs
 TARGETS += pidfd
 TARGETS += powerpc
index 91f9aea853b1438438f2b8a72c19da8a8edf4e62..997c65dcad6891a3e65e41df25d236bc820d60f7 100644 (file)
@@ -23,4 +23,8 @@ so_txtime
 tcp_fastopen_backup_key
 nettest
 fin_ack_lat
-reuseaddr_ports_exhausted
\ No newline at end of file
+reuseaddr_ports_exhausted
+hwtstamp_config
+rxtimestamp
+timestamping
+txtimestamp
index 24d8424010ebdf62cd79cfa87df2b642f3b90ec9..3f386eb9e7d7702b6c61e14307dca72e663cf25d 100644 (file)
@@ -15,6 +15,7 @@ TEST_PROGS += fin_ack_lat.sh fib_nexthop_multiprefix.sh fib_nexthops.sh
 TEST_PROGS += altnames.sh icmp_redirect.sh ip6_gre_headroom.sh
 TEST_PROGS += route_localnet.sh
 TEST_PROGS += reuseaddr_ports_exhausted.sh
+TEST_PROGS += txtimestamp.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket nettest
 TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
@@ -23,9 +24,10 @@ TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx ip_defrag
 TEST_GEN_FILES += so_txtime ipv6_flowlabel ipv6_flowlabel_mgr
 TEST_GEN_FILES += tcp_fastopen_backup_key
 TEST_GEN_FILES += fin_ack_lat
+TEST_GEN_FILES += reuseaddr_ports_exhausted
+TEST_GEN_FILES += hwtstamp_config rxtimestamp timestamping txtimestamp
 TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
 TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
-TEST_GEN_FILES += reuseaddr_ports_exhausted
 
 KSFT_KHDR_INSTALL := 1
 include ../lib.mk
index b8503a8119b070a27f22b2eada4d890b119e8d8a..3b42c06b59858225978e42f23cc1dd9ea5ad8e36 100644 (file)
@@ -12,6 +12,7 @@ CONFIG_IPV6_VTI=y
 CONFIG_DUMMY=y
 CONFIG_BRIDGE=y
 CONFIG_VLAN_8021Q=y
+CONFIG_IFB=y
 CONFIG_NETFILTER=y
 CONFIG_NETFILTER_ADVANCED=y
 CONFIG_NF_CONNTRACK=m
@@ -27,5 +28,6 @@ CONFIG_NFT_CHAIN_NAT_IPV6=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NET_SCH_FQ=m
 CONFIG_NET_SCH_ETF=m
+CONFIG_NET_SCH_NETEM=y
 CONFIG_TEST_BLACKHOLE_DEV=m
 CONFIG_KALLSYMS=y
diff --git a/tools/testing/selftests/net/hwtstamp_config.c b/tools/testing/selftests/net/hwtstamp_config.c
new file mode 100644 (file)
index 0000000..e1fdee8
--- /dev/null
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test program for SIOC{G,S}HWTSTAMP
+ * Copyright 2013 Solarflare Communications
+ * Author: Ben Hutchings
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <linux/if.h>
+#include <linux/net_tstamp.h>
+#include <linux/sockios.h>
+
+static int
+lookup_value(const char **names, int size, const char *name)
+{
+       int value;
+
+       for (value = 0; value < size; value++)
+               if (names[value] && strcasecmp(names[value], name) == 0)
+                       return value;
+
+       return -1;
+}
+
+static const char *
+lookup_name(const char **names, int size, int value)
+{
+       return (value >= 0 && value < size) ? names[value] : NULL;
+}
+
+static void list_names(FILE *f, const char **names, int size)
+{
+       int value;
+
+       for (value = 0; value < size; value++)
+               if (names[value])
+                       fprintf(f, "    %s\n", names[value]);
+}
+
+static const char *tx_types[] = {
+#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name
+       TX_TYPE(OFF),
+       TX_TYPE(ON),
+       TX_TYPE(ONESTEP_SYNC)
+#undef TX_TYPE
+};
+#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0])))
+
+static const char *rx_filters[] = {
+#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name
+       RX_FILTER(NONE),
+       RX_FILTER(ALL),
+       RX_FILTER(SOME),
+       RX_FILTER(PTP_V1_L4_EVENT),
+       RX_FILTER(PTP_V1_L4_SYNC),
+       RX_FILTER(PTP_V1_L4_DELAY_REQ),
+       RX_FILTER(PTP_V2_L4_EVENT),
+       RX_FILTER(PTP_V2_L4_SYNC),
+       RX_FILTER(PTP_V2_L4_DELAY_REQ),
+       RX_FILTER(PTP_V2_L2_EVENT),
+       RX_FILTER(PTP_V2_L2_SYNC),
+       RX_FILTER(PTP_V2_L2_DELAY_REQ),
+       RX_FILTER(PTP_V2_EVENT),
+       RX_FILTER(PTP_V2_SYNC),
+       RX_FILTER(PTP_V2_DELAY_REQ),
+#undef RX_FILTER
+};
+#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0])))
+
+static void usage(void)
+{
+       fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n"
+             "tx_type is any of (case-insensitive):\n",
+             stderr);
+       list_names(stderr, tx_types, N_TX_TYPES);
+       fputs("rx_filter is any of (case-insensitive):\n", stderr);
+       list_names(stderr, rx_filters, N_RX_FILTERS);
+}
+
+int main(int argc, char **argv)
+{
+       struct ifreq ifr;
+       struct hwtstamp_config config;
+       const char *name;
+       int sock;
+
+       if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) {
+               usage();
+               return 2;
+       }
+
+       if (argc == 4) {
+               config.flags = 0;
+               config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]);
+               config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]);
+               if (config.tx_type < 0 || config.rx_filter < 0) {
+                       usage();
+                       return 2;
+               }
+       }
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock < 0) {
+               perror("socket");
+               return 1;
+       }
+
+       strcpy(ifr.ifr_name, argv[1]);
+       ifr.ifr_data = (caddr_t)&config;
+
+       if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) {
+               perror("ioctl");
+               return 1;
+       }
+
+       printf("flags = %#x\n", config.flags);
+       name = lookup_name(tx_types, N_TX_TYPES, config.tx_type);
+       if (name)
+               printf("tx_type = %s\n", name);
+       else
+               printf("tx_type = %d\n", config.tx_type);
+       name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter);
+       if (name)
+               printf("rx_filter = %s\n", name);
+       else
+               printf("rx_filter = %d\n", config.rx_filter);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/net/rxtimestamp.c b/tools/testing/selftests/net/rxtimestamp.c
new file mode 100644 (file)
index 0000000..6dee9e6
--- /dev/null
@@ -0,0 +1,390 @@
+#include <errno.h>
+#include <error.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <asm/types.h>
+#include <linux/net_tstamp.h>
+#include <linux/errqueue.h>
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+struct options {
+       int so_timestamp;
+       int so_timestampns;
+       int so_timestamping;
+};
+
+struct tstamps {
+       bool tstamp;
+       bool tstampns;
+       bool swtstamp;
+       bool hwtstamp;
+};
+
+struct socket_type {
+       char *friendly_name;
+       int type;
+       int protocol;
+       bool enabled;
+};
+
+struct test_case {
+       struct options sockopt;
+       struct tstamps expected;
+       bool enabled;
+};
+
+struct sof_flag {
+       int mask;
+       char *name;
+};
+
+static struct sof_flag sof_flags[] = {
+#define SOF_FLAG(f) { f, #f }
+       SOF_FLAG(SOF_TIMESTAMPING_SOFTWARE),
+       SOF_FLAG(SOF_TIMESTAMPING_RX_SOFTWARE),
+       SOF_FLAG(SOF_TIMESTAMPING_RX_HARDWARE),
+};
+
+static struct socket_type socket_types[] = {
+       { "ip",         SOCK_RAW,       IPPROTO_EGP },
+       { "udp",        SOCK_DGRAM,     IPPROTO_UDP },
+       { "tcp",        SOCK_STREAM,    IPPROTO_TCP },
+};
+
+static struct test_case test_cases[] = {
+       { {}, {} },
+       {
+               { so_timestamp: 1 },
+               { tstamp: true }
+       },
+       {
+               { so_timestampns: 1 },
+               { tstampns: true }
+       },
+       {
+               { so_timestamp: 1, so_timestampns: 1 },
+               { tstampns: true }
+       },
+       {
+               { so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE },
+               {}
+       },
+       {
+               /* Loopback device does not support hw timestamps. */
+               { so_timestamping: SOF_TIMESTAMPING_RX_HARDWARE },
+               {}
+       },
+       {
+               { so_timestamping: SOF_TIMESTAMPING_SOFTWARE },
+               {}
+       },
+       {
+               { so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE
+                       | SOF_TIMESTAMPING_RX_HARDWARE },
+               {}
+       },
+       {
+               { so_timestamping: SOF_TIMESTAMPING_SOFTWARE
+                       | SOF_TIMESTAMPING_RX_SOFTWARE },
+               { swtstamp: true }
+       },
+       {
+               { so_timestamp: 1, so_timestamping: SOF_TIMESTAMPING_SOFTWARE
+                       | SOF_TIMESTAMPING_RX_SOFTWARE },
+               { tstamp: true, swtstamp: true }
+       },
+};
+
+static struct option long_options[] = {
+       { "list_tests", no_argument, 0, 'l' },
+       { "test_num", required_argument, 0, 'n' },
+       { "op_size", required_argument, 0, 's' },
+       { "tcp", no_argument, 0, 't' },
+       { "udp", no_argument, 0, 'u' },
+       { "ip", no_argument, 0, 'i' },
+};
+
+static int next_port = 19999;
+static int op_size = 10 * 1024;
+
+void print_test_case(struct test_case *t)
+{
+       int f = 0;
+
+       printf("sockopts {");
+       if (t->sockopt.so_timestamp)
+               printf(" SO_TIMESTAMP ");
+       if (t->sockopt.so_timestampns)
+               printf(" SO_TIMESTAMPNS ");
+       if (t->sockopt.so_timestamping) {
+               printf(" SO_TIMESTAMPING: {");
+               for (f = 0; f < ARRAY_SIZE(sof_flags); f++)
+                       if (t->sockopt.so_timestamping & sof_flags[f].mask)
+                               printf(" %s |", sof_flags[f].name);
+               printf("}");
+       }
+       printf("} expected cmsgs: {");
+       if (t->expected.tstamp)
+               printf(" SCM_TIMESTAMP ");
+       if (t->expected.tstampns)
+               printf(" SCM_TIMESTAMPNS ");
+       if (t->expected.swtstamp || t->expected.hwtstamp) {
+               printf(" SCM_TIMESTAMPING {");
+               if (t->expected.swtstamp)
+                       printf("0");
+               if (t->expected.swtstamp && t->expected.hwtstamp)
+                       printf(",");
+               if (t->expected.hwtstamp)
+                       printf("2");
+               printf("}");
+       }
+       printf("}\n");
+}
+
+void do_send(int src)
+{
+       int r;
+       char *buf = malloc(op_size);
+
+       memset(buf, 'z', op_size);
+       r = write(src, buf, op_size);
+       if (r < 0)
+               error(1, errno, "Failed to sendmsg");
+
+       free(buf);
+}
+
+bool do_recv(int rcv, int read_size, struct tstamps expected)
+{
+       const int CMSG_SIZE = 1024;
+
+       struct scm_timestamping *ts;
+       struct tstamps actual = {};
+       char cmsg_buf[CMSG_SIZE];
+       struct iovec recv_iov;
+       struct cmsghdr *cmsg;
+       bool failed = false;
+       struct msghdr hdr;
+       int flags = 0;
+       int r;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.msg_iov = &recv_iov;
+       hdr.msg_iovlen = 1;
+       recv_iov.iov_base = malloc(read_size);
+       recv_iov.iov_len = read_size;
+
+       hdr.msg_control = cmsg_buf;
+       hdr.msg_controllen = sizeof(cmsg_buf);
+
+       r = recvmsg(rcv, &hdr, flags);
+       if (r < 0)
+               error(1, errno, "Failed to recvmsg");
+       if (r != read_size)
+               error(1, 0, "Only received %d bytes of payload.", r);
+
+       if (hdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
+               error(1, 0, "Message was truncated.");
+
+       for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
+            cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
+               if (cmsg->cmsg_level != SOL_SOCKET)
+                       error(1, 0, "Unexpected cmsg_level %d",
+                             cmsg->cmsg_level);
+               switch (cmsg->cmsg_type) {
+               case SCM_TIMESTAMP:
+                       actual.tstamp = true;
+                       break;
+               case SCM_TIMESTAMPNS:
+                       actual.tstampns = true;
+                       break;
+               case SCM_TIMESTAMPING:
+                       ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
+                       actual.swtstamp = !!ts->ts[0].tv_sec;
+                       if (ts->ts[1].tv_sec != 0)
+                               error(0, 0, "ts[1] should not be set.");
+                       actual.hwtstamp = !!ts->ts[2].tv_sec;
+                       break;
+               default:
+                       error(1, 0, "Unexpected cmsg_type %d", cmsg->cmsg_type);
+               }
+       }
+
+#define VALIDATE(field) \
+       do { \
+               if (expected.field != actual.field) { \
+                       if (expected.field) \
+                               error(0, 0, "Expected " #field " to be set."); \
+                       else \
+                               error(0, 0, \
+                                     "Expected " #field " to not be set."); \
+                       failed = true; \
+               } \
+       } while (0)
+
+       VALIDATE(tstamp);
+       VALIDATE(tstampns);
+       VALIDATE(swtstamp);
+       VALIDATE(hwtstamp);
+#undef VALIDATE
+
+       free(recv_iov.iov_base);
+
+       return failed;
+}
+
+void config_so_flags(int rcv, struct options o)
+{
+       int on = 1;
+
+       if (setsockopt(rcv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+               error(1, errno, "Failed to enable SO_REUSEADDR");
+
+       if (o.so_timestamp &&
+           setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMP,
+                      &o.so_timestamp, sizeof(o.so_timestamp)) < 0)
+               error(1, errno, "Failed to enable SO_TIMESTAMP");
+
+       if (o.so_timestampns &&
+           setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPNS,
+                      &o.so_timestampns, sizeof(o.so_timestampns)) < 0)
+               error(1, errno, "Failed to enable SO_TIMESTAMPNS");
+
+       if (o.so_timestamping &&
+           setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPING,
+                      &o.so_timestamping, sizeof(o.so_timestamping)) < 0)
+               error(1, errno, "Failed to set SO_TIMESTAMPING");
+}
+
+bool run_test_case(struct socket_type s, struct test_case t)
+{
+       int port = (s.type == SOCK_RAW) ? 0 : next_port++;
+       int read_size = op_size;
+       struct sockaddr_in addr;
+       bool failed = false;
+       int src, dst, rcv;
+
+       src = socket(AF_INET, s.type, s.protocol);
+       if (src < 0)
+               error(1, errno, "Failed to open src socket");
+
+       dst = socket(AF_INET, s.type, s.protocol);
+       if (dst < 0)
+               error(1, errno, "Failed to open dst socket");
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       addr.sin_port = htons(port);
+
+       if (bind(dst, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+               error(1, errno, "Failed to bind to port %d", port);
+
+       if (s.type == SOCK_STREAM && (listen(dst, 1) < 0))
+               error(1, errno, "Failed to listen");
+
+       if (connect(src, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+               error(1, errno, "Failed to connect");
+
+       if (s.type == SOCK_STREAM) {
+               rcv = accept(dst, NULL, NULL);
+               if (rcv < 0)
+                       error(1, errno, "Failed to accept");
+               close(dst);
+       } else {
+               rcv = dst;
+       }
+
+       config_so_flags(rcv, t.sockopt);
+       usleep(20000); /* setsockopt for SO_TIMESTAMPING is asynchronous */
+       do_send(src);
+
+       if (s.type == SOCK_RAW)
+               read_size += 20;  /* for IP header */
+       failed = do_recv(rcv, read_size, t.expected);
+
+       close(rcv);
+       close(src);
+
+       return failed;
+}
+
+int main(int argc, char **argv)
+{
+       bool all_protocols = true;
+       bool all_tests = true;
+       int arg_index = 0;
+       int failures = 0;
+       int s, t;
+       char opt;
+
+       while ((opt = getopt_long(argc, argv, "", long_options,
+                                 &arg_index)) != -1) {
+               switch (opt) {
+               case 'l':
+                       for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
+                               printf("%d\t", t);
+                               print_test_case(&test_cases[t]);
+                       }
+                       return 0;
+               case 'n':
+                       t = atoi(optarg);
+                       if (t >= ARRAY_SIZE(test_cases))
+                               error(1, 0, "Invalid test case: %d", t);
+                       all_tests = false;
+                       test_cases[t].enabled = true;
+                       break;
+               case 's':
+                       op_size = atoi(optarg);
+                       break;
+               case 't':
+                       all_protocols = false;
+                       socket_types[2].enabled = true;
+                       break;
+               case 'u':
+                       all_protocols = false;
+                       socket_types[1].enabled = true;
+                       break;
+               case 'i':
+                       all_protocols = false;
+                       socket_types[0].enabled = true;
+                       break;
+               default:
+                       error(1, 0, "Failed to parse parameters.");
+               }
+       }
+
+       for (s = 0; s < ARRAY_SIZE(socket_types); s++) {
+               if (!all_protocols && !socket_types[s].enabled)
+                       continue;
+
+               printf("Testing %s...\n", socket_types[s].friendly_name);
+               for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
+                       if (!all_tests && !test_cases[t].enabled)
+                               continue;
+
+                       printf("Starting testcase %d...\n", t);
+                       if (run_test_case(socket_types[s], test_cases[t])) {
+                               failures++;
+                               printf("FAILURE in test case ");
+                               print_test_case(&test_cases[t]);
+                       }
+               }
+       }
+       if (!failures)
+               printf("PASSED.\n");
+       return failures;
+}
diff --git a/tools/testing/selftests/net/timestamping.c b/tools/testing/selftests/net/timestamping.c
new file mode 100644 (file)
index 0000000..aca3491
--- /dev/null
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This program demonstrates how the various time stamping features in
+ * the Linux kernel work. It emulates the behavior of a PTP
+ * implementation in stand-alone master mode by sending PTPv1 Sync
+ * multicasts once every second. It looks for similar packets, but
+ * beyond that doesn't actually implement PTP.
+ *
+ * Outgoing packets are time stamped with SO_TIMESTAMPING with or
+ * without hardware support.
+ *
+ * Incoming packets are time stamped with SO_TIMESTAMPING with or
+ * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
+ * SO_TIMESTAMP[NS].
+ *
+ * Copyright (C) 2009 Intel Corporation.
+ * Author: Patrick Ohly <patrick.ohly@intel.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <asm/types.h>
+#include <linux/net_tstamp.h>
+#include <linux/errqueue.h>
+#include <linux/sockios.h>
+
+#ifndef SO_TIMESTAMPING
+# define SO_TIMESTAMPING         37
+# define SCM_TIMESTAMPING        SO_TIMESTAMPING
+#endif
+
+#ifndef SO_TIMESTAMPNS
+# define SO_TIMESTAMPNS 35
+#endif
+
+static void usage(const char *error)
+{
+       if (error)
+               printf("invalid option: %s\n", error);
+       printf("timestamping interface option*\n\n"
+              "Options:\n"
+              "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
+              "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
+              "  SO_TIMESTAMPNS - more accurate software time stamping\n"
+              "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
+              "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
+              "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
+              "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
+              "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
+              "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
+              "  SIOCGSTAMP - check last socket time stamp\n"
+              "  SIOCGSTAMPNS - more accurate socket time stamp\n");
+       exit(1);
+}
+
+static void bail(const char *error)
+{
+       printf("%s: %s\n", error, strerror(errno));
+       exit(1);
+}
+
+static const unsigned char sync[] = {
+       0x00, 0x01, 0x00, 0x01,
+       0x5f, 0x44, 0x46, 0x4c,
+       0x54, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x01, 0x01,
+
+       /* fake uuid */
+       0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05,
+
+       0x00, 0x01, 0x00, 0x37,
+       0x00, 0x00, 0x00, 0x08,
+       0x00, 0x00, 0x00, 0x00,
+       0x49, 0x05, 0xcd, 0x01,
+       0x29, 0xb1, 0x8d, 0xb0,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x01,
+
+       /* fake uuid */
+       0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05,
+
+       0x00, 0x00, 0x00, 0x37,
+       0x00, 0x00, 0x00, 0x04,
+       0x44, 0x46, 0x4c, 0x54,
+       0x00, 0x00, 0xf0, 0x60,
+       0x00, 0x01, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x01,
+       0x00, 0x00, 0xf0, 0x60,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x04,
+       0x44, 0x46, 0x4c, 0x54,
+       0x00, 0x01,
+
+       /* fake uuid */
+       0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05,
+
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00
+};
+
+static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
+{
+       struct timeval now;
+       int res;
+
+       res = sendto(sock, sync, sizeof(sync), 0,
+               addr, addr_len);
+       gettimeofday(&now, 0);
+       if (res < 0)
+               printf("%s: %s\n", "send", strerror(errno));
+       else
+               printf("%ld.%06ld: sent %d bytes\n",
+                      (long)now.tv_sec, (long)now.tv_usec,
+                      res);
+}
+
+static void printpacket(struct msghdr *msg, int res,
+                       char *data,
+                       int sock, int recvmsg_flags,
+                       int siocgstamp, int siocgstampns)
+{
+       struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
+       struct cmsghdr *cmsg;
+       struct timeval tv;
+       struct timespec ts;
+       struct timeval now;
+
+       gettimeofday(&now, 0);
+
+       printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
+              (long)now.tv_sec, (long)now.tv_usec,
+              (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
+              res,
+              inet_ntoa(from_addr->sin_addr),
+              msg->msg_controllen);
+       for (cmsg = CMSG_FIRSTHDR(msg);
+            cmsg;
+            cmsg = CMSG_NXTHDR(msg, cmsg)) {
+               printf("   cmsg len %zu: ", cmsg->cmsg_len);
+               switch (cmsg->cmsg_level) {
+               case SOL_SOCKET:
+                       printf("SOL_SOCKET ");
+                       switch (cmsg->cmsg_type) {
+                       case SO_TIMESTAMP: {
+                               struct timeval *stamp =
+                                       (struct timeval *)CMSG_DATA(cmsg);
+                               printf("SO_TIMESTAMP %ld.%06ld",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_usec);
+                               break;
+                       }
+                       case SO_TIMESTAMPNS: {
+                               struct timespec *stamp =
+                                       (struct timespec *)CMSG_DATA(cmsg);
+                               printf("SO_TIMESTAMPNS %ld.%09ld",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_nsec);
+                               break;
+                       }
+                       case SO_TIMESTAMPING: {
+                               struct timespec *stamp =
+                                       (struct timespec *)CMSG_DATA(cmsg);
+                               printf("SO_TIMESTAMPING ");
+                               printf("SW %ld.%09ld ",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_nsec);
+                               stamp++;
+                               /* skip deprecated HW transformed */
+                               stamp++;
+                               printf("HW raw %ld.%09ld",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_nsec);
+                               break;
+                       }
+                       default:
+                               printf("type %d", cmsg->cmsg_type);
+                               break;
+                       }
+                       break;
+               case IPPROTO_IP:
+                       printf("IPPROTO_IP ");
+                       switch (cmsg->cmsg_type) {
+                       case IP_RECVERR: {
+                               struct sock_extended_err *err =
+                                       (struct sock_extended_err *)CMSG_DATA(cmsg);
+                               printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
+                                       strerror(err->ee_errno),
+                                       err->ee_origin,
+#ifdef SO_EE_ORIGIN_TIMESTAMPING
+                                       err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
+                                       "bounced packet" : "unexpected origin"
+#else
+                                       "probably SO_EE_ORIGIN_TIMESTAMPING"
+#endif
+                                       );
+                               if (res < sizeof(sync))
+                                       printf(" => truncated data?!");
+                               else if (!memcmp(sync, data + res - sizeof(sync),
+                                                       sizeof(sync)))
+                                       printf(" => GOT OUR DATA BACK (HURRAY!)");
+                               break;
+                       }
+                       case IP_PKTINFO: {
+                               struct in_pktinfo *pktinfo =
+                                       (struct in_pktinfo *)CMSG_DATA(cmsg);
+                               printf("IP_PKTINFO interface index %u",
+                                       pktinfo->ipi_ifindex);
+                               break;
+                       }
+                       default:
+                               printf("type %d", cmsg->cmsg_type);
+                               break;
+                       }
+                       break;
+               default:
+                       printf("level %d type %d",
+                               cmsg->cmsg_level,
+                               cmsg->cmsg_type);
+                       break;
+               }
+               printf("\n");
+       }
+
+       if (siocgstamp) {
+               if (ioctl(sock, SIOCGSTAMP, &tv))
+                       printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
+               else
+                       printf("SIOCGSTAMP %ld.%06ld\n",
+                              (long)tv.tv_sec,
+                              (long)tv.tv_usec);
+       }
+       if (siocgstampns) {
+               if (ioctl(sock, SIOCGSTAMPNS, &ts))
+                       printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
+               else
+                       printf("SIOCGSTAMPNS %ld.%09ld\n",
+                              (long)ts.tv_sec,
+                              (long)ts.tv_nsec);
+       }
+}
+
+static void recvpacket(int sock, int recvmsg_flags,
+                      int siocgstamp, int siocgstampns)
+{
+       char data[256];
+       struct msghdr msg;
+       struct iovec entry;
+       struct sockaddr_in from_addr;
+       struct {
+               struct cmsghdr cm;
+               char control[512];
+       } control;
+       int res;
+
+       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 = (caddr_t)&from_addr;
+       msg.msg_namelen = sizeof(from_addr);
+       msg.msg_control = &control;
+       msg.msg_controllen = sizeof(control);
+
+       res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
+       if (res < 0) {
+               printf("%s %s: %s\n",
+                      "recvmsg",
+                      (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
+                      strerror(errno));
+       } else {
+               printpacket(&msg, res, data,
+                           sock, recvmsg_flags,
+                           siocgstamp, siocgstampns);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       int so_timestamping_flags = 0;
+       int so_timestamp = 0;
+       int so_timestampns = 0;
+       int siocgstamp = 0;
+       int siocgstampns = 0;
+       int ip_multicast_loop = 0;
+       char *interface;
+       int i;
+       int enabled = 1;
+       int sock;
+       struct ifreq device;
+       struct ifreq hwtstamp;
+       struct hwtstamp_config hwconfig, hwconfig_requested;
+       struct sockaddr_in addr;
+       struct ip_mreq imr;
+       struct in_addr iaddr;
+       int val;
+       socklen_t len;
+       struct timeval next;
+
+       if (argc < 2)
+               usage(0);
+       interface = argv[1];
+
+       for (i = 2; i < argc; i++) {
+               if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
+                       so_timestamp = 1;
+               else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
+                       so_timestampns = 1;
+               else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
+                       siocgstamp = 1;
+               else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
+                       siocgstampns = 1;
+               else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
+                       ip_multicast_loop = 1;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
+               else
+                       usage(argv[i]);
+       }
+
+       sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (sock < 0)
+               bail("socket");
+
+       memset(&device, 0, sizeof(device));
+       strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
+       if (ioctl(sock, SIOCGIFADDR, &device) < 0)
+               bail("getting interface IP address");
+
+       memset(&hwtstamp, 0, sizeof(hwtstamp));
+       strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
+       hwtstamp.ifr_data = (void *)&hwconfig;
+       memset(&hwconfig, 0, sizeof(hwconfig));
+       hwconfig.tx_type =
+               (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
+               HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       hwconfig.rx_filter =
+               (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
+               HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
+       hwconfig_requested = hwconfig;
+       if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
+               if ((errno == EINVAL || errno == ENOTSUP) &&
+                   hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
+                   hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
+                       printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
+               else
+                       bail("SIOCSHWTSTAMP");
+       }
+       printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
+              hwconfig_requested.tx_type, hwconfig.tx_type,
+              hwconfig_requested.rx_filter, hwconfig.rx_filter);
+
+       /* bind to PTP port */
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = htons(319 /* PTP event port */);
+       if (bind(sock,
+                (struct sockaddr *)&addr,
+                sizeof(struct sockaddr_in)) < 0)
+               bail("bind");
+
+       /* set multicast group for outgoing packets */
+       inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
+       addr.sin_addr = iaddr;
+       imr.imr_multiaddr.s_addr = iaddr.s_addr;
+       imr.imr_interface.s_addr =
+               ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
+       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
+                      &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
+               bail("set multicast");
+
+       /* join multicast group, loop our own packet */
+       if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                      &imr, sizeof(struct ip_mreq)) < 0)
+               bail("join multicast group");
+
+       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
+                      &ip_multicast_loop, sizeof(enabled)) < 0) {
+               bail("loop multicast");
+       }
+
+       /* set socket options for time stamping */
+       if (so_timestamp &&
+               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
+                          &enabled, sizeof(enabled)) < 0)
+               bail("setsockopt SO_TIMESTAMP");
+
+       if (so_timestampns &&
+               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
+                          &enabled, sizeof(enabled)) < 0)
+               bail("setsockopt SO_TIMESTAMPNS");
+
+       if (so_timestamping_flags &&
+               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
+                          &so_timestamping_flags,
+                          sizeof(so_timestamping_flags)) < 0)
+               bail("setsockopt SO_TIMESTAMPING");
+
+       /* request IP_PKTINFO for debugging purposes */
+       if (setsockopt(sock, SOL_IP, IP_PKTINFO,
+                      &enabled, sizeof(enabled)) < 0)
+               printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
+
+       /* verify socket options */
+       len = sizeof(val);
+       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
+               printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
+       else
+               printf("SO_TIMESTAMP %d\n", val);
+
+       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
+               printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
+                      strerror(errno));
+       else
+               printf("SO_TIMESTAMPNS %d\n", val);
+
+       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
+               printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
+                      strerror(errno));
+       } else {
+               printf("SO_TIMESTAMPING %d\n", val);
+               if (val != so_timestamping_flags)
+                       printf("   not the expected value %d\n",
+                              so_timestamping_flags);
+       }
+
+       /* send packets forever every five seconds */
+       gettimeofday(&next, 0);
+       next.tv_sec = (next.tv_sec + 1) / 5 * 5;
+       next.tv_usec = 0;
+       while (1) {
+               struct timeval now;
+               struct timeval delta;
+               long delta_us;
+               int res;
+               fd_set readfs, errorfs;
+
+               gettimeofday(&now, 0);
+               delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
+                       (long)(next.tv_usec - now.tv_usec);
+               if (delta_us > 0) {
+                       /* continue waiting for timeout or data */
+                       delta.tv_sec = delta_us / 1000000;
+                       delta.tv_usec = delta_us % 1000000;
+
+                       FD_ZERO(&readfs);
+                       FD_ZERO(&errorfs);
+                       FD_SET(sock, &readfs);
+                       FD_SET(sock, &errorfs);
+                       printf("%ld.%06ld: select %ldus\n",
+                              (long)now.tv_sec, (long)now.tv_usec,
+                              delta_us);
+                       res = select(sock + 1, &readfs, 0, &errorfs, &delta);
+                       gettimeofday(&now, 0);
+                       printf("%ld.%06ld: select returned: %d, %s\n",
+                              (long)now.tv_sec, (long)now.tv_usec,
+                              res,
+                              res < 0 ? strerror(errno) : "success");
+                       if (res > 0) {
+                               if (FD_ISSET(sock, &readfs))
+                                       printf("ready for reading\n");
+                               if (FD_ISSET(sock, &errorfs))
+                                       printf("has error\n");
+                               recvpacket(sock, 0,
+                                          siocgstamp,
+                                          siocgstampns);
+                               recvpacket(sock, MSG_ERRQUEUE,
+                                          siocgstamp,
+                                          siocgstampns);
+                       }
+               } else {
+                       /* write one packet */
+                       sendpacket(sock,
+                                  (struct sockaddr *)&addr,
+                                  sizeof(addr));
+                       next.tv_sec += 5;
+                       continue;
+               }
+       }
+
+       return 0;
+}
diff --git a/tools/testing/selftests/net/txtimestamp.c b/tools/testing/selftests/net/txtimestamp.c
new file mode 100644 (file)
index 0000000..011b0da
--- /dev/null
@@ -0,0 +1,916 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2014 Google Inc.
+ * Author: willemb@google.com (Willem de Bruijn)
+ *
+ * Test software tx timestamping, including
+ *
+ * - SCHED, SND and ACK timestamps
+ * - RAW, UDP and TCP
+ * - IPv4 and IPv6
+ * - various packet sizes (to test GSO and TSO)
+ *
+ * Consult the command line arguments for help on running
+ * the various testcases.
+ *
+ * This test requires a dummy TCP server.
+ * A simple `nc6 [-u] -l -p $DESTPORT` will do
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <error.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/errqueue.h>
+#include <linux/if_ether.h>
+#include <linux/ipv6.h>
+#include <linux/net_tstamp.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netpacket/packet.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#define NSEC_PER_USEC  1000L
+#define USEC_PER_SEC   1000000L
+#define NSEC_PER_SEC   1000000000LL
+
+/* command line parameters */
+static int cfg_proto = SOCK_STREAM;
+static int cfg_ipproto = IPPROTO_TCP;
+static int cfg_num_pkts = 4;
+static int do_ipv4 = 1;
+static int do_ipv6 = 1;
+static int cfg_payload_len = 10;
+static int cfg_poll_timeout = 100;
+static int cfg_delay_snd;
+static int cfg_delay_ack;
+static bool cfg_show_payload;
+static bool cfg_do_pktinfo;
+static bool cfg_busy_poll;
+static int cfg_sleep_usec = 50 * 1000;
+static bool cfg_loop_nodata;
+static bool cfg_use_cmsg;
+static bool cfg_use_pf_packet;
+static bool cfg_use_epoll;
+static bool cfg_epollet;
+static bool cfg_do_listen;
+static uint16_t dest_port = 9000;
+static bool cfg_print_nsec;
+
+static struct sockaddr_in daddr;
+static struct sockaddr_in6 daddr6;
+static struct timespec ts_usr;
+
+static int saved_tskey = -1;
+static int saved_tskey_type = -1;
+
+struct timing_event {
+       int64_t min;
+       int64_t max;
+       int64_t total;
+       int count;
+};
+
+static struct timing_event usr_enq;
+static struct timing_event usr_snd;
+static struct timing_event usr_ack;
+
+static bool test_failed;
+
+static int64_t timespec_to_ns64(struct timespec *ts)
+{
+       return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
+}
+
+static int64_t timespec_to_us64(struct timespec *ts)
+{
+       return ts->tv_sec * USEC_PER_SEC + ts->tv_nsec / NSEC_PER_USEC;
+}
+
+static void init_timing_event(struct timing_event *te)
+{
+       te->min = INT64_MAX;
+       te->max = 0;
+       te->total = 0;
+       te->count = 0;
+}
+
+static void add_timing_event(struct timing_event *te,
+               struct timespec *t_start, struct timespec *t_end)
+{
+       int64_t ts_delta = timespec_to_ns64(t_end) - timespec_to_ns64(t_start);
+
+       te->count++;
+       if (ts_delta < te->min)
+               te->min = ts_delta;
+       if (ts_delta > te->max)
+               te->max = ts_delta;
+       te->total += ts_delta;
+}
+
+static void validate_key(int tskey, int tstype)
+{
+       int stepsize;
+
+       /* compare key for each subsequent request
+        * must only test for one type, the first one requested
+        */
+       if (saved_tskey == -1)
+               saved_tskey_type = tstype;
+       else if (saved_tskey_type != tstype)
+               return;
+
+       stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1;
+       if (tskey != saved_tskey + stepsize) {
+               fprintf(stderr, "ERROR: key %d, expected %d\n",
+                               tskey, saved_tskey + stepsize);
+               test_failed = true;
+       }
+
+       saved_tskey = tskey;
+}
+
+static void validate_timestamp(struct timespec *cur, int min_delay)
+{
+       int max_delay = min_delay + 500 /* processing time upper bound */;
+       int64_t cur64, start64;
+
+       cur64 = timespec_to_us64(cur);
+       start64 = timespec_to_us64(&ts_usr);
+
+       if (cur64 < start64 + min_delay || cur64 > start64 + max_delay) {
+               fprintf(stderr, "ERROR: %lu us expected between %d and %d\n",
+                               cur64 - start64, min_delay, max_delay);
+               test_failed = true;
+       }
+}
+
+static void __print_ts_delta_formatted(int64_t ts_delta)
+{
+       if (cfg_print_nsec)
+               fprintf(stderr, "%lu ns", ts_delta);
+       else
+               fprintf(stderr, "%lu us", ts_delta / NSEC_PER_USEC);
+}
+
+static void __print_timestamp(const char *name, struct timespec *cur,
+                             uint32_t key, int payload_len)
+{
+       int64_t ts_delta;
+
+       if (!(cur->tv_sec | cur->tv_nsec))
+               return;
+
+       if (cfg_print_nsec)
+               fprintf(stderr, "  %s: %lu s %lu ns (seq=%u, len=%u)",
+                               name, cur->tv_sec, cur->tv_nsec,
+                               key, payload_len);
+       else
+               fprintf(stderr, "  %s: %lu s %lu us (seq=%u, len=%u)",
+                               name, cur->tv_sec, cur->tv_nsec / NSEC_PER_USEC,
+                               key, payload_len);
+
+       if (cur != &ts_usr) {
+               ts_delta = timespec_to_ns64(cur) - timespec_to_ns64(&ts_usr);
+               fprintf(stderr, "  (USR +");
+               __print_ts_delta_formatted(ts_delta);
+               fprintf(stderr, ")");
+       }
+
+       fprintf(stderr, "\n");
+}
+
+static void print_timestamp_usr(void)
+{
+       if (clock_gettime(CLOCK_REALTIME, &ts_usr))
+               error(1, errno, "clock_gettime");
+
+       __print_timestamp("  USR", &ts_usr, 0, 0);
+}
+
+static void print_timestamp(struct scm_timestamping *tss, int tstype,
+                           int tskey, int payload_len)
+{
+       const char *tsname;
+
+       validate_key(tskey, tstype);
+
+       switch (tstype) {
+       case SCM_TSTAMP_SCHED:
+               tsname = "  ENQ";
+               validate_timestamp(&tss->ts[0], 0);
+               add_timing_event(&usr_enq, &ts_usr, &tss->ts[0]);
+               break;
+       case SCM_TSTAMP_SND:
+               tsname = "  SND";
+               validate_timestamp(&tss->ts[0], cfg_delay_snd);
+               add_timing_event(&usr_snd, &ts_usr, &tss->ts[0]);
+               break;
+       case SCM_TSTAMP_ACK:
+               tsname = "  ACK";
+               validate_timestamp(&tss->ts[0], cfg_delay_ack);
+               add_timing_event(&usr_ack, &ts_usr, &tss->ts[0]);
+               break;
+       default:
+               error(1, 0, "unknown timestamp type: %u",
+               tstype);
+       }
+       __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
+}
+
+static void print_timing_event(char *name, struct timing_event *te)
+{
+       if (!te->count)
+               return;
+
+       fprintf(stderr, "    %s: count=%d", name, te->count);
+       fprintf(stderr, ", avg=");
+       __print_ts_delta_formatted((int64_t)(te->total / te->count));
+       fprintf(stderr, ", min=");
+       __print_ts_delta_formatted(te->min);
+       fprintf(stderr, ", max=");
+       __print_ts_delta_formatted(te->max);
+       fprintf(stderr, "\n");
+}
+
+/* TODO: convert to check_and_print payload once API is stable */
+static void print_payload(char *data, int len)
+{
+       int i;
+
+       if (!len)
+               return;
+
+       if (len > 70)
+               len = 70;
+
+       fprintf(stderr, "payload: ");
+       for (i = 0; i < len; i++)
+               fprintf(stderr, "%02hhx ", data[i]);
+       fprintf(stderr, "\n");
+}
+
+static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
+{
+       char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
+
+       fprintf(stderr, "         pktinfo: ifindex=%u src=%s dst=%s\n",
+               ifindex,
+               saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
+               daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
+}
+
+static void __epoll(int epfd)
+{
+       struct epoll_event events;
+       int ret;
+
+       memset(&events, 0, sizeof(events));
+       ret = epoll_wait(epfd, &events, 1, cfg_poll_timeout);
+       if (ret != 1)
+               error(1, errno, "epoll_wait");
+}
+
+static void __poll(int fd)
+{
+       struct pollfd pollfd;
+       int ret;
+
+       memset(&pollfd, 0, sizeof(pollfd));
+       pollfd.fd = fd;
+       ret = poll(&pollfd, 1, cfg_poll_timeout);
+       if (ret != 1)
+               error(1, errno, "poll");
+}
+
+static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
+{
+       struct sock_extended_err *serr = NULL;
+       struct scm_timestamping *tss = NULL;
+       struct cmsghdr *cm;
+       int batch = 0;
+
+       for (cm = CMSG_FIRSTHDR(msg);
+            cm && cm->cmsg_len;
+            cm = CMSG_NXTHDR(msg, cm)) {
+               if (cm->cmsg_level == SOL_SOCKET &&
+                   cm->cmsg_type == SCM_TIMESTAMPING) {
+                       tss = (void *) CMSG_DATA(cm);
+               } else if ((cm->cmsg_level == SOL_IP &&
+                           cm->cmsg_type == IP_RECVERR) ||
+                          (cm->cmsg_level == SOL_IPV6 &&
+                           cm->cmsg_type == IPV6_RECVERR) ||
+                          (cm->cmsg_level == SOL_PACKET &&
+                           cm->cmsg_type == PACKET_TX_TIMESTAMP)) {
+                       serr = (void *) CMSG_DATA(cm);
+                       if (serr->ee_errno != ENOMSG ||
+                           serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
+                               fprintf(stderr, "unknown ip error %d %d\n",
+                                               serr->ee_errno,
+                                               serr->ee_origin);
+                               serr = NULL;
+                       }
+               } else if (cm->cmsg_level == SOL_IP &&
+                          cm->cmsg_type == IP_PKTINFO) {
+                       struct in_pktinfo *info = (void *) CMSG_DATA(cm);
+                       print_pktinfo(AF_INET, info->ipi_ifindex,
+                                     &info->ipi_spec_dst, &info->ipi_addr);
+               } else if (cm->cmsg_level == SOL_IPV6 &&
+                          cm->cmsg_type == IPV6_PKTINFO) {
+                       struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
+                       print_pktinfo(AF_INET6, info6->ipi6_ifindex,
+                                     NULL, &info6->ipi6_addr);
+               } else
+                       fprintf(stderr, "unknown cmsg %d,%d\n",
+                                       cm->cmsg_level, cm->cmsg_type);
+
+               if (serr && tss) {
+                       print_timestamp(tss, serr->ee_info, serr->ee_data,
+                                       payload_len);
+                       serr = NULL;
+                       tss = NULL;
+                       batch++;
+               }
+       }
+
+       if (batch > 1)
+               fprintf(stderr, "batched %d timestamps\n", batch);
+}
+
+static int recv_errmsg(int fd)
+{
+       static char ctrl[1024 /* overprovision*/];
+       static struct msghdr msg;
+       struct iovec entry;
+       static char *data;
+       int ret = 0;
+
+       data = malloc(cfg_payload_len);
+       if (!data)
+               error(1, 0, "malloc");
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&entry, 0, sizeof(entry));
+       memset(ctrl, 0, sizeof(ctrl));
+
+       entry.iov_base = data;
+       entry.iov_len = cfg_payload_len;
+       msg.msg_iov = &entry;
+       msg.msg_iovlen = 1;
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+       msg.msg_control = ctrl;
+       msg.msg_controllen = sizeof(ctrl);
+
+       ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
+       if (ret == -1 && errno != EAGAIN)
+               error(1, errno, "recvmsg");
+
+       if (ret >= 0) {
+               __recv_errmsg_cmsg(&msg, ret);
+               if (cfg_show_payload)
+                       print_payload(data, cfg_payload_len);
+       }
+
+       free(data);
+       return ret == -1;
+}
+
+static uint16_t get_ip_csum(const uint16_t *start, int num_words,
+                           unsigned long sum)
+{
+       int i;
+
+       for (i = 0; i < num_words; i++)
+               sum += start[i];
+
+       while (sum >> 16)
+               sum = (sum & 0xFFFF) + (sum >> 16);
+
+       return ~sum;
+}
+
+static uint16_t get_udp_csum(const struct udphdr *udph, int alen)
+{
+       unsigned long pseudo_sum, csum_len;
+       const void *csum_start = udph;
+
+       pseudo_sum = htons(IPPROTO_UDP);
+       pseudo_sum += udph->len;
+
+       /* checksum ip(v6) addresses + udp header + payload */
+       csum_start -= alen * 2;
+       csum_len = ntohs(udph->len) + alen * 2;
+
+       return get_ip_csum(csum_start, csum_len >> 1, pseudo_sum);
+}
+
+static int fill_header_ipv4(void *p)
+{
+       struct iphdr *iph = p;
+
+       memset(iph, 0, sizeof(*iph));
+
+       iph->ihl        = 5;
+       iph->version    = 4;
+       iph->ttl        = 2;
+       iph->saddr      = daddr.sin_addr.s_addr;        /* set for udp csum calc */
+       iph->daddr      = daddr.sin_addr.s_addr;
+       iph->protocol   = IPPROTO_UDP;
+
+       /* kernel writes saddr, csum, len */
+
+       return sizeof(*iph);
+}
+
+static int fill_header_ipv6(void *p)
+{
+       struct ipv6hdr *ip6h = p;
+
+       memset(ip6h, 0, sizeof(*ip6h));
+
+       ip6h->version           = 6;
+       ip6h->payload_len       = htons(sizeof(struct udphdr) + cfg_payload_len);
+       ip6h->nexthdr           = IPPROTO_UDP;
+       ip6h->hop_limit         = 64;
+
+       ip6h->saddr             = daddr6.sin6_addr;
+       ip6h->daddr             = daddr6.sin6_addr;
+
+       /* kernel does not write saddr in case of ipv6 */
+
+       return sizeof(*ip6h);
+}
+
+static void fill_header_udp(void *p, bool is_ipv4)
+{
+       struct udphdr *udph = p;
+
+       udph->source = ntohs(dest_port + 1);    /* spoof */
+       udph->dest   = ntohs(dest_port);
+       udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
+       udph->check  = 0;
+
+       udph->check  = get_udp_csum(udph, is_ipv4 ? sizeof(struct in_addr) :
+                                                   sizeof(struct in6_addr));
+}
+
+static void do_test(int family, unsigned int report_opt)
+{
+       char control[CMSG_SPACE(sizeof(uint32_t))];
+       struct sockaddr_ll laddr;
+       unsigned int sock_opt;
+       struct cmsghdr *cmsg;
+       struct msghdr msg;
+       struct iovec iov;
+       char *buf;
+       int fd, i, val = 1, total_len, epfd = 0;
+
+       init_timing_event(&usr_enq);
+       init_timing_event(&usr_snd);
+       init_timing_event(&usr_ack);
+
+       total_len = cfg_payload_len;
+       if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
+               total_len += sizeof(struct udphdr);
+               if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW)
+                       if (family == PF_INET)
+                               total_len += sizeof(struct iphdr);
+                       else
+                               total_len += sizeof(struct ipv6hdr);
+
+               /* special case, only rawv6_sendmsg:
+                * pass proto in sin6_port if not connected
+                * also see ANK comment in net/ipv4/raw.c
+                */
+               daddr6.sin6_port = htons(cfg_ipproto);
+       }
+
+       buf = malloc(total_len);
+       if (!buf)
+               error(1, 0, "malloc");
+
+       fd = socket(cfg_use_pf_packet ? PF_PACKET : family,
+                   cfg_proto, cfg_ipproto);
+       if (fd < 0)
+               error(1, errno, "socket");
+
+       if (cfg_use_epoll) {
+               struct epoll_event ev;
+
+               memset(&ev, 0, sizeof(ev));
+               ev.data.fd = fd;
+               if (cfg_epollet)
+                       ev.events |= EPOLLET;
+               epfd = epoll_create(1);
+               if (epfd <= 0)
+                       error(1, errno, "epoll_create");
+               if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
+                       error(1, errno, "epoll_ctl");
+       }
+
+       /* reset expected key on each new socket */
+       saved_tskey = -1;
+
+       if (cfg_proto == SOCK_STREAM) {
+               if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
+                              (char*) &val, sizeof(val)))
+                       error(1, 0, "setsockopt no nagle");
+
+               if (family == PF_INET) {
+                       if (connect(fd, (void *) &daddr, sizeof(daddr)))
+                               error(1, errno, "connect ipv4");
+               } else {
+                       if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
+                               error(1, errno, "connect ipv6");
+               }
+       }
+
+       if (cfg_do_pktinfo) {
+               if (family == AF_INET6) {
+                       if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
+                                      &val, sizeof(val)))
+                               error(1, errno, "setsockopt pktinfo ipv6");
+               } else {
+                       if (setsockopt(fd, SOL_IP, IP_PKTINFO,
+                                      &val, sizeof(val)))
+                               error(1, errno, "setsockopt pktinfo ipv4");
+               }
+       }
+
+       sock_opt = SOF_TIMESTAMPING_SOFTWARE |
+                  SOF_TIMESTAMPING_OPT_CMSG |
+                  SOF_TIMESTAMPING_OPT_ID;
+
+       if (!cfg_use_cmsg)
+               sock_opt |= report_opt;
+
+       if (cfg_loop_nodata)
+               sock_opt |= SOF_TIMESTAMPING_OPT_TSONLY;
+
+       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
+                      (char *) &sock_opt, sizeof(sock_opt)))
+               error(1, 0, "setsockopt timestamping");
+
+       for (i = 0; i < cfg_num_pkts; i++) {
+               memset(&msg, 0, sizeof(msg));
+               memset(buf, 'a' + i, total_len);
+
+               if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
+                       int off = 0;
+
+                       if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) {
+                               if (family == PF_INET)
+                                       off = fill_header_ipv4(buf);
+                               else
+                                       off = fill_header_ipv6(buf);
+                       }
+
+                       fill_header_udp(buf + off, family == PF_INET);
+               }
+
+               print_timestamp_usr();
+
+               iov.iov_base = buf;
+               iov.iov_len = total_len;
+
+               if (cfg_proto != SOCK_STREAM) {
+                       if (cfg_use_pf_packet) {
+                               memset(&laddr, 0, sizeof(laddr));
+
+                               laddr.sll_family        = AF_PACKET;
+                               laddr.sll_ifindex       = 1;
+                               laddr.sll_protocol      = htons(family == AF_INET ? ETH_P_IP : ETH_P_IPV6);
+                               laddr.sll_halen         = ETH_ALEN;
+
+                               msg.msg_name = (void *)&laddr;
+                               msg.msg_namelen = sizeof(laddr);
+                       } else if (family == PF_INET) {
+                               msg.msg_name = (void *)&daddr;
+                               msg.msg_namelen = sizeof(daddr);
+                       } else {
+                               msg.msg_name = (void *)&daddr6;
+                               msg.msg_namelen = sizeof(daddr6);
+                       }
+               }
+
+               msg.msg_iov = &iov;
+               msg.msg_iovlen = 1;
+
+               if (cfg_use_cmsg) {
+                       memset(control, 0, sizeof(control));
+
+                       msg.msg_control = control;
+                       msg.msg_controllen = sizeof(control);
+
+                       cmsg = CMSG_FIRSTHDR(&msg);
+                       cmsg->cmsg_level = SOL_SOCKET;
+                       cmsg->cmsg_type = SO_TIMESTAMPING;
+                       cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+
+                       *((uint32_t *) CMSG_DATA(cmsg)) = report_opt;
+               }
+
+               val = sendmsg(fd, &msg, 0);
+               if (val != total_len)
+                       error(1, errno, "send");
+
+               /* wait for all errors to be queued, else ACKs arrive OOO */
+               if (cfg_sleep_usec)
+                       usleep(cfg_sleep_usec);
+
+               if (!cfg_busy_poll) {
+                       if (cfg_use_epoll)
+                               __epoll(epfd);
+                       else
+                               __poll(fd);
+               }
+
+               while (!recv_errmsg(fd)) {}
+       }
+
+       print_timing_event("USR-ENQ", &usr_enq);
+       print_timing_event("USR-SND", &usr_snd);
+       print_timing_event("USR-ACK", &usr_ack);
+
+       if (close(fd))
+               error(1, errno, "close");
+
+       free(buf);
+       usleep(100 * NSEC_PER_USEC);
+}
+
+static void __attribute__((noreturn)) usage(const char *filepath)
+{
+       fprintf(stderr, "\nUsage: %s [options] hostname\n"
+                       "\nwhere options are:\n"
+                       "  -4:   only IPv4\n"
+                       "  -6:   only IPv6\n"
+                       "  -h:   show this message\n"
+                       "  -b:   busy poll to read from error queue\n"
+                       "  -c N: number of packets for each test\n"
+                       "  -C:   use cmsg to set tstamp recording options\n"
+                       "  -e:   use level-triggered epoll() instead of poll()\n"
+                       "  -E:   use event-triggered epoll() instead of poll()\n"
+                       "  -F:   poll()/epoll() waits forever for an event\n"
+                       "  -I:   request PKTINFO\n"
+                       "  -l N: send N bytes at a time\n"
+                       "  -L    listen on hostname and port\n"
+                       "  -n:   set no-payload option\n"
+                       "  -N:   print timestamps and durations in nsec (instead of usec)\n"
+                       "  -p N: connect to port N\n"
+                       "  -P:   use PF_PACKET\n"
+                       "  -r:   use raw\n"
+                       "  -R:   use raw (IP_HDRINCL)\n"
+                       "  -S N: usec to sleep before reading error queue\n"
+                       "  -u:   use udp\n"
+                       "  -v:   validate SND delay (usec)\n"
+                       "  -V:   validate ACK delay (usec)\n"
+                       "  -x:   show payload (up to 70 bytes)\n",
+                       filepath);
+       exit(1);
+}
+
+static void parse_opt(int argc, char **argv)
+{
+       int proto_count = 0;
+       int c;
+
+       while ((c = getopt(argc, argv,
+                               "46bc:CeEFhIl:LnNp:PrRS:uv:V:x")) != -1) {
+               switch (c) {
+               case '4':
+                       do_ipv6 = 0;
+                       break;
+               case '6':
+                       do_ipv4 = 0;
+                       break;
+               case 'b':
+                       cfg_busy_poll = true;
+                       break;
+               case 'c':
+                       cfg_num_pkts = strtoul(optarg, NULL, 10);
+                       break;
+               case 'C':
+                       cfg_use_cmsg = true;
+                       break;
+               case 'e':
+                       cfg_use_epoll = true;
+                       break;
+               case 'E':
+                       cfg_use_epoll = true;
+                       cfg_epollet = true;
+               case 'F':
+                       cfg_poll_timeout = -1;
+                       break;
+               case 'I':
+                       cfg_do_pktinfo = true;
+                       break;
+               case 'l':
+                       cfg_payload_len = strtoul(optarg, NULL, 10);
+                       break;
+               case 'L':
+                       cfg_do_listen = true;
+                       break;
+               case 'n':
+                       cfg_loop_nodata = true;
+                       break;
+               case 'N':
+                       cfg_print_nsec = true;
+                       break;
+               case 'p':
+                       dest_port = strtoul(optarg, NULL, 10);
+                       break;
+               case 'P':
+                       proto_count++;
+                       cfg_use_pf_packet = true;
+                       cfg_proto = SOCK_DGRAM;
+                       cfg_ipproto = 0;
+                       break;
+               case 'r':
+                       proto_count++;
+                       cfg_proto = SOCK_RAW;
+                       cfg_ipproto = IPPROTO_UDP;
+                       break;
+               case 'R':
+                       proto_count++;
+                       cfg_proto = SOCK_RAW;
+                       cfg_ipproto = IPPROTO_RAW;
+                       break;
+               case 'S':
+                       cfg_sleep_usec = strtoul(optarg, NULL, 10);
+                       break;
+               case 'u':
+                       proto_count++;
+                       cfg_proto = SOCK_DGRAM;
+                       cfg_ipproto = IPPROTO_UDP;
+                       break;
+               case 'v':
+                       cfg_delay_snd = strtoul(optarg, NULL, 10);
+                       break;
+               case 'V':
+                       cfg_delay_ack = strtoul(optarg, NULL, 10);
+                       break;
+               case 'x':
+                       cfg_show_payload = true;
+                       break;
+               case 'h':
+               default:
+                       usage(argv[0]);
+               }
+       }
+
+       if (!cfg_payload_len)
+               error(1, 0, "payload may not be nonzero");
+       if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
+               error(1, 0, "udp packet might exceed expected MTU");
+       if (!do_ipv4 && !do_ipv6)
+               error(1, 0, "pass -4 or -6, not both");
+       if (proto_count > 1)
+               error(1, 0, "pass -P, -r, -R or -u, not multiple");
+       if (cfg_do_pktinfo && cfg_use_pf_packet)
+               error(1, 0, "cannot ask for pktinfo over pf_packet");
+       if (cfg_busy_poll && cfg_use_epoll)
+               error(1, 0, "pass epoll or busy_poll, not both");
+
+       if (optind != argc - 1)
+               error(1, 0, "missing required hostname argument");
+}
+
+static void resolve_hostname(const char *hostname)
+{
+       struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 };
+       struct addrinfo *addrs, *cur;
+       int have_ipv4 = 0, have_ipv6 = 0;
+
+retry:
+       if (getaddrinfo(hostname, NULL, &hints, &addrs))
+               error(1, errno, "getaddrinfo");
+
+       cur = addrs;
+       while (cur && !have_ipv4 && !have_ipv6) {
+               if (!have_ipv4 && cur->ai_family == AF_INET) {
+                       memcpy(&daddr, cur->ai_addr, sizeof(daddr));
+                       daddr.sin_port = htons(dest_port);
+                       have_ipv4 = 1;
+               }
+               else if (!have_ipv6 && cur->ai_family == AF_INET6) {
+                       memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
+                       daddr6.sin6_port = htons(dest_port);
+                       have_ipv6 = 1;
+               }
+               cur = cur->ai_next;
+       }
+       if (addrs)
+               freeaddrinfo(addrs);
+
+       if (do_ipv6 && hints.ai_family != AF_INET6) {
+               hints.ai_family = AF_INET6;
+               goto retry;
+       }
+
+       do_ipv4 &= have_ipv4;
+       do_ipv6 &= have_ipv6;
+}
+
+static void do_listen(int family, void *addr, int alen)
+{
+       int fd, type;
+
+       type = cfg_proto == SOCK_RAW ? SOCK_DGRAM : cfg_proto;
+
+       fd = socket(family, type, 0);
+       if (fd == -1)
+               error(1, errno, "socket rx");
+
+       if (bind(fd, addr, alen))
+               error(1, errno, "bind rx");
+
+       if (type == SOCK_STREAM && listen(fd, 10))
+               error(1, errno, "listen rx");
+
+       /* leave fd open, will be closed on process exit.
+        * this enables connect() to succeed and avoids icmp replies
+        */
+}
+
+static void do_main(int family)
+{
+       fprintf(stderr, "family:       %s %s\n",
+                       family == PF_INET ? "INET" : "INET6",
+                       cfg_use_pf_packet ? "(PF_PACKET)" : "");
+
+       fprintf(stderr, "test SND\n");
+       do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
+
+       fprintf(stderr, "test ENQ\n");
+       do_test(family, SOF_TIMESTAMPING_TX_SCHED);
+
+       fprintf(stderr, "test ENQ + SND\n");
+       do_test(family, SOF_TIMESTAMPING_TX_SCHED |
+                       SOF_TIMESTAMPING_TX_SOFTWARE);
+
+       if (cfg_proto == SOCK_STREAM) {
+               fprintf(stderr, "\ntest ACK\n");
+               do_test(family, SOF_TIMESTAMPING_TX_ACK);
+
+               fprintf(stderr, "\ntest SND + ACK\n");
+               do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
+                               SOF_TIMESTAMPING_TX_ACK);
+
+               fprintf(stderr, "\ntest ENQ + SND + ACK\n");
+               do_test(family, SOF_TIMESTAMPING_TX_SCHED |
+                               SOF_TIMESTAMPING_TX_SOFTWARE |
+                               SOF_TIMESTAMPING_TX_ACK);
+       }
+}
+
+const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
+
+int main(int argc, char **argv)
+{
+       if (argc == 1)
+               usage(argv[0]);
+
+       parse_opt(argc, argv);
+       resolve_hostname(argv[argc - 1]);
+
+       fprintf(stderr, "protocol:     %s\n", sock_names[cfg_proto]);
+       fprintf(stderr, "payload:      %u\n", cfg_payload_len);
+       fprintf(stderr, "server port:  %u\n", dest_port);
+       fprintf(stderr, "\n");
+
+       if (do_ipv4) {
+               if (cfg_do_listen)
+                       do_listen(PF_INET, &daddr, sizeof(daddr));
+               do_main(PF_INET);
+       }
+
+       if (do_ipv6) {
+               if (cfg_do_listen)
+                       do_listen(PF_INET6, &daddr6, sizeof(daddr6));
+               do_main(PF_INET6);
+       }
+
+       return test_failed;
+}
diff --git a/tools/testing/selftests/net/txtimestamp.sh b/tools/testing/selftests/net/txtimestamp.sh
new file mode 100755 (executable)
index 0000000..eea6f51
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Send packets with transmit timestamps over loopback with netem
+# Verify that timestamps correspond to netem delay
+
+set -e
+
+setup() {
+       # set 1ms delay on lo egress
+       tc qdisc add dev lo root netem delay 1ms
+
+       # set 2ms delay on ifb0 egress
+       modprobe ifb
+       ip link add ifb_netem0 type ifb
+       ip link set dev ifb_netem0 up
+       tc qdisc add dev ifb_netem0 root netem delay 2ms
+
+       # redirect lo ingress through ifb0 egress
+       tc qdisc add dev lo handle ffff: ingress
+       tc filter add dev lo parent ffff: \
+               u32 match mark 0 0xffff \
+               action mirred egress redirect dev ifb_netem0
+}
+
+run_test_v4v6() {
+       # SND will be delayed 1000us
+       # ACK will be delayed 6000us: 1 + 2 ms round-trip
+       local -r args="$@ -v 1000 -V 6000"
+
+       ./txtimestamp ${args} -4 -L 127.0.0.1
+       ./txtimestamp ${args} -6 -L ::1
+}
+
+run_test_tcpudpraw() {
+       local -r args=$@
+
+       run_test_v4v6 ${args}           # tcp
+       run_test_v4v6 ${args} -u        # udp
+       run_test_v4v6 ${args} -r        # raw
+       run_test_v4v6 ${args} -R        # raw (IPPROTO_RAW)
+       run_test_v4v6 ${args} -P        # pf_packet
+}
+
+run_test_all() {
+       setup
+       run_test_tcpudpraw              # setsockopt
+       run_test_tcpudpraw -C           # cmsg
+       run_test_tcpudpraw -n           # timestamp w/o data
+       echo "OK. All tests passed"
+}
+
+run_test_one() {
+       setup
+       ./txtimestamp $@
+}
+
+usage() {
+       echo "Usage: $0 [ -r | --run ] <txtimestamp args> | [ -h | --help ]"
+       echo "  (no args)  Run all tests"
+       echo "  -r|--run  Run an individual test with arguments"
+       echo "  -h|--help Help"
+}
+
+main() {
+       if [[ $# -eq 0 ]]; then
+               run_test_all
+       else
+               if [[ "$1" = "-r" || "$1" == "--run" ]]; then
+                       shift
+                       run_test_one $@
+               else
+                       usage
+               fi
+       fi
+}
+
+if [[ "$(ip netns identify)" == "root" ]]; then
+       ./in_netns.sh $0 $@
+else
+       main $@
+fi
diff --git a/tools/testing/selftests/networking/timestamping/.gitignore b/tools/testing/selftests/networking/timestamping/.gitignore
deleted file mode 100644 (file)
index d935503..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-timestamping
-rxtimestamp
-txtimestamp
-hwtstamp_config
diff --git a/tools/testing/selftests/networking/timestamping/Makefile b/tools/testing/selftests/networking/timestamping/Makefile
deleted file mode 100644 (file)
index 1de8bd8..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -I../../../../../usr/include
-
-TEST_GEN_FILES := hwtstamp_config rxtimestamp timestamping txtimestamp
-TEST_PROGS := txtimestamp.sh
-
-all: $(TEST_PROGS)
-
-top_srcdir = ../../../../..
-KSFT_KHDR_INSTALL := 1
-include ../../lib.mk
diff --git a/tools/testing/selftests/networking/timestamping/config b/tools/testing/selftests/networking/timestamping/config
deleted file mode 100644 (file)
index a13e316..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-CONFIG_IFB=y
-CONFIG_NET_SCH_NETEM=y
diff --git a/tools/testing/selftests/networking/timestamping/hwtstamp_config.c b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c
deleted file mode 100644 (file)
index e1fdee8..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Test program for SIOC{G,S}HWTSTAMP
- * Copyright 2013 Solarflare Communications
- * Author: Ben Hutchings
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-
-#include <linux/if.h>
-#include <linux/net_tstamp.h>
-#include <linux/sockios.h>
-
-static int
-lookup_value(const char **names, int size, const char *name)
-{
-       int value;
-
-       for (value = 0; value < size; value++)
-               if (names[value] && strcasecmp(names[value], name) == 0)
-                       return value;
-
-       return -1;
-}
-
-static const char *
-lookup_name(const char **names, int size, int value)
-{
-       return (value >= 0 && value < size) ? names[value] : NULL;
-}
-
-static void list_names(FILE *f, const char **names, int size)
-{
-       int value;
-
-       for (value = 0; value < size; value++)
-               if (names[value])
-                       fprintf(f, "    %s\n", names[value]);
-}
-
-static const char *tx_types[] = {
-#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name
-       TX_TYPE(OFF),
-       TX_TYPE(ON),
-       TX_TYPE(ONESTEP_SYNC)
-#undef TX_TYPE
-};
-#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0])))
-
-static const char *rx_filters[] = {
-#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name
-       RX_FILTER(NONE),
-       RX_FILTER(ALL),
-       RX_FILTER(SOME),
-       RX_FILTER(PTP_V1_L4_EVENT),
-       RX_FILTER(PTP_V1_L4_SYNC),
-       RX_FILTER(PTP_V1_L4_DELAY_REQ),
-       RX_FILTER(PTP_V2_L4_EVENT),
-       RX_FILTER(PTP_V2_L4_SYNC),
-       RX_FILTER(PTP_V2_L4_DELAY_REQ),
-       RX_FILTER(PTP_V2_L2_EVENT),
-       RX_FILTER(PTP_V2_L2_SYNC),
-       RX_FILTER(PTP_V2_L2_DELAY_REQ),
-       RX_FILTER(PTP_V2_EVENT),
-       RX_FILTER(PTP_V2_SYNC),
-       RX_FILTER(PTP_V2_DELAY_REQ),
-#undef RX_FILTER
-};
-#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0])))
-
-static void usage(void)
-{
-       fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n"
-             "tx_type is any of (case-insensitive):\n",
-             stderr);
-       list_names(stderr, tx_types, N_TX_TYPES);
-       fputs("rx_filter is any of (case-insensitive):\n", stderr);
-       list_names(stderr, rx_filters, N_RX_FILTERS);
-}
-
-int main(int argc, char **argv)
-{
-       struct ifreq ifr;
-       struct hwtstamp_config config;
-       const char *name;
-       int sock;
-
-       if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) {
-               usage();
-               return 2;
-       }
-
-       if (argc == 4) {
-               config.flags = 0;
-               config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]);
-               config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]);
-               if (config.tx_type < 0 || config.rx_filter < 0) {
-                       usage();
-                       return 2;
-               }
-       }
-
-       sock = socket(AF_INET, SOCK_DGRAM, 0);
-       if (sock < 0) {
-               perror("socket");
-               return 1;
-       }
-
-       strcpy(ifr.ifr_name, argv[1]);
-       ifr.ifr_data = (caddr_t)&config;
-
-       if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) {
-               perror("ioctl");
-               return 1;
-       }
-
-       printf("flags = %#x\n", config.flags);
-       name = lookup_name(tx_types, N_TX_TYPES, config.tx_type);
-       if (name)
-               printf("tx_type = %s\n", name);
-       else
-               printf("tx_type = %d\n", config.tx_type);
-       name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter);
-       if (name)
-               printf("rx_filter = %s\n", name);
-       else
-               printf("rx_filter = %d\n", config.rx_filter);
-
-       return 0;
-}
diff --git a/tools/testing/selftests/networking/timestamping/rxtimestamp.c b/tools/testing/selftests/networking/timestamping/rxtimestamp.c
deleted file mode 100644 (file)
index 6dee9e6..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-#include <errno.h>
-#include <error.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-
-#include <asm/types.h>
-#include <linux/net_tstamp.h>
-#include <linux/errqueue.h>
-
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-
-struct options {
-       int so_timestamp;
-       int so_timestampns;
-       int so_timestamping;
-};
-
-struct tstamps {
-       bool tstamp;
-       bool tstampns;
-       bool swtstamp;
-       bool hwtstamp;
-};
-
-struct socket_type {
-       char *friendly_name;
-       int type;
-       int protocol;
-       bool enabled;
-};
-
-struct test_case {
-       struct options sockopt;
-       struct tstamps expected;
-       bool enabled;
-};
-
-struct sof_flag {
-       int mask;
-       char *name;
-};
-
-static struct sof_flag sof_flags[] = {
-#define SOF_FLAG(f) { f, #f }
-       SOF_FLAG(SOF_TIMESTAMPING_SOFTWARE),
-       SOF_FLAG(SOF_TIMESTAMPING_RX_SOFTWARE),
-       SOF_FLAG(SOF_TIMESTAMPING_RX_HARDWARE),
-};
-
-static struct socket_type socket_types[] = {
-       { "ip",         SOCK_RAW,       IPPROTO_EGP },
-       { "udp",        SOCK_DGRAM,     IPPROTO_UDP },
-       { "tcp",        SOCK_STREAM,    IPPROTO_TCP },
-};
-
-static struct test_case test_cases[] = {
-       { {}, {} },
-       {
-               { so_timestamp: 1 },
-               { tstamp: true }
-       },
-       {
-               { so_timestampns: 1 },
-               { tstampns: true }
-       },
-       {
-               { so_timestamp: 1, so_timestampns: 1 },
-               { tstampns: true }
-       },
-       {
-               { so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE },
-               {}
-       },
-       {
-               /* Loopback device does not support hw timestamps. */
-               { so_timestamping: SOF_TIMESTAMPING_RX_HARDWARE },
-               {}
-       },
-       {
-               { so_timestamping: SOF_TIMESTAMPING_SOFTWARE },
-               {}
-       },
-       {
-               { so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE
-                       | SOF_TIMESTAMPING_RX_HARDWARE },
-               {}
-       },
-       {
-               { so_timestamping: SOF_TIMESTAMPING_SOFTWARE
-                       | SOF_TIMESTAMPING_RX_SOFTWARE },
-               { swtstamp: true }
-       },
-       {
-               { so_timestamp: 1, so_timestamping: SOF_TIMESTAMPING_SOFTWARE
-                       | SOF_TIMESTAMPING_RX_SOFTWARE },
-               { tstamp: true, swtstamp: true }
-       },
-};
-
-static struct option long_options[] = {
-       { "list_tests", no_argument, 0, 'l' },
-       { "test_num", required_argument, 0, 'n' },
-       { "op_size", required_argument, 0, 's' },
-       { "tcp", no_argument, 0, 't' },
-       { "udp", no_argument, 0, 'u' },
-       { "ip", no_argument, 0, 'i' },
-};
-
-static int next_port = 19999;
-static int op_size = 10 * 1024;
-
-void print_test_case(struct test_case *t)
-{
-       int f = 0;
-
-       printf("sockopts {");
-       if (t->sockopt.so_timestamp)
-               printf(" SO_TIMESTAMP ");
-       if (t->sockopt.so_timestampns)
-               printf(" SO_TIMESTAMPNS ");
-       if (t->sockopt.so_timestamping) {
-               printf(" SO_TIMESTAMPING: {");
-               for (f = 0; f < ARRAY_SIZE(sof_flags); f++)
-                       if (t->sockopt.so_timestamping & sof_flags[f].mask)
-                               printf(" %s |", sof_flags[f].name);
-               printf("}");
-       }
-       printf("} expected cmsgs: {");
-       if (t->expected.tstamp)
-               printf(" SCM_TIMESTAMP ");
-       if (t->expected.tstampns)
-               printf(" SCM_TIMESTAMPNS ");
-       if (t->expected.swtstamp || t->expected.hwtstamp) {
-               printf(" SCM_TIMESTAMPING {");
-               if (t->expected.swtstamp)
-                       printf("0");
-               if (t->expected.swtstamp && t->expected.hwtstamp)
-                       printf(",");
-               if (t->expected.hwtstamp)
-                       printf("2");
-               printf("}");
-       }
-       printf("}\n");
-}
-
-void do_send(int src)
-{
-       int r;
-       char *buf = malloc(op_size);
-
-       memset(buf, 'z', op_size);
-       r = write(src, buf, op_size);
-       if (r < 0)
-               error(1, errno, "Failed to sendmsg");
-
-       free(buf);
-}
-
-bool do_recv(int rcv, int read_size, struct tstamps expected)
-{
-       const int CMSG_SIZE = 1024;
-
-       struct scm_timestamping *ts;
-       struct tstamps actual = {};
-       char cmsg_buf[CMSG_SIZE];
-       struct iovec recv_iov;
-       struct cmsghdr *cmsg;
-       bool failed = false;
-       struct msghdr hdr;
-       int flags = 0;
-       int r;
-
-       memset(&hdr, 0, sizeof(hdr));
-       hdr.msg_iov = &recv_iov;
-       hdr.msg_iovlen = 1;
-       recv_iov.iov_base = malloc(read_size);
-       recv_iov.iov_len = read_size;
-
-       hdr.msg_control = cmsg_buf;
-       hdr.msg_controllen = sizeof(cmsg_buf);
-
-       r = recvmsg(rcv, &hdr, flags);
-       if (r < 0)
-               error(1, errno, "Failed to recvmsg");
-       if (r != read_size)
-               error(1, 0, "Only received %d bytes of payload.", r);
-
-       if (hdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
-               error(1, 0, "Message was truncated.");
-
-       for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
-            cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
-               if (cmsg->cmsg_level != SOL_SOCKET)
-                       error(1, 0, "Unexpected cmsg_level %d",
-                             cmsg->cmsg_level);
-               switch (cmsg->cmsg_type) {
-               case SCM_TIMESTAMP:
-                       actual.tstamp = true;
-                       break;
-               case SCM_TIMESTAMPNS:
-                       actual.tstampns = true;
-                       break;
-               case SCM_TIMESTAMPING:
-                       ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
-                       actual.swtstamp = !!ts->ts[0].tv_sec;
-                       if (ts->ts[1].tv_sec != 0)
-                               error(0, 0, "ts[1] should not be set.");
-                       actual.hwtstamp = !!ts->ts[2].tv_sec;
-                       break;
-               default:
-                       error(1, 0, "Unexpected cmsg_type %d", cmsg->cmsg_type);
-               }
-       }
-
-#define VALIDATE(field) \
-       do { \
-               if (expected.field != actual.field) { \
-                       if (expected.field) \
-                               error(0, 0, "Expected " #field " to be set."); \
-                       else \
-                               error(0, 0, \
-                                     "Expected " #field " to not be set."); \
-                       failed = true; \
-               } \
-       } while (0)
-
-       VALIDATE(tstamp);
-       VALIDATE(tstampns);
-       VALIDATE(swtstamp);
-       VALIDATE(hwtstamp);
-#undef VALIDATE
-
-       free(recv_iov.iov_base);
-
-       return failed;
-}
-
-void config_so_flags(int rcv, struct options o)
-{
-       int on = 1;
-
-       if (setsockopt(rcv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
-               error(1, errno, "Failed to enable SO_REUSEADDR");
-
-       if (o.so_timestamp &&
-           setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMP,
-                      &o.so_timestamp, sizeof(o.so_timestamp)) < 0)
-               error(1, errno, "Failed to enable SO_TIMESTAMP");
-
-       if (o.so_timestampns &&
-           setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPNS,
-                      &o.so_timestampns, sizeof(o.so_timestampns)) < 0)
-               error(1, errno, "Failed to enable SO_TIMESTAMPNS");
-
-       if (o.so_timestamping &&
-           setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPING,
-                      &o.so_timestamping, sizeof(o.so_timestamping)) < 0)
-               error(1, errno, "Failed to set SO_TIMESTAMPING");
-}
-
-bool run_test_case(struct socket_type s, struct test_case t)
-{
-       int port = (s.type == SOCK_RAW) ? 0 : next_port++;
-       int read_size = op_size;
-       struct sockaddr_in addr;
-       bool failed = false;
-       int src, dst, rcv;
-
-       src = socket(AF_INET, s.type, s.protocol);
-       if (src < 0)
-               error(1, errno, "Failed to open src socket");
-
-       dst = socket(AF_INET, s.type, s.protocol);
-       if (dst < 0)
-               error(1, errno, "Failed to open dst socket");
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-       addr.sin_port = htons(port);
-
-       if (bind(dst, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-               error(1, errno, "Failed to bind to port %d", port);
-
-       if (s.type == SOCK_STREAM && (listen(dst, 1) < 0))
-               error(1, errno, "Failed to listen");
-
-       if (connect(src, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-               error(1, errno, "Failed to connect");
-
-       if (s.type == SOCK_STREAM) {
-               rcv = accept(dst, NULL, NULL);
-               if (rcv < 0)
-                       error(1, errno, "Failed to accept");
-               close(dst);
-       } else {
-               rcv = dst;
-       }
-
-       config_so_flags(rcv, t.sockopt);
-       usleep(20000); /* setsockopt for SO_TIMESTAMPING is asynchronous */
-       do_send(src);
-
-       if (s.type == SOCK_RAW)
-               read_size += 20;  /* for IP header */
-       failed = do_recv(rcv, read_size, t.expected);
-
-       close(rcv);
-       close(src);
-
-       return failed;
-}
-
-int main(int argc, char **argv)
-{
-       bool all_protocols = true;
-       bool all_tests = true;
-       int arg_index = 0;
-       int failures = 0;
-       int s, t;
-       char opt;
-
-       while ((opt = getopt_long(argc, argv, "", long_options,
-                                 &arg_index)) != -1) {
-               switch (opt) {
-               case 'l':
-                       for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
-                               printf("%d\t", t);
-                               print_test_case(&test_cases[t]);
-                       }
-                       return 0;
-               case 'n':
-                       t = atoi(optarg);
-                       if (t >= ARRAY_SIZE(test_cases))
-                               error(1, 0, "Invalid test case: %d", t);
-                       all_tests = false;
-                       test_cases[t].enabled = true;
-                       break;
-               case 's':
-                       op_size = atoi(optarg);
-                       break;
-               case 't':
-                       all_protocols = false;
-                       socket_types[2].enabled = true;
-                       break;
-               case 'u':
-                       all_protocols = false;
-                       socket_types[1].enabled = true;
-                       break;
-               case 'i':
-                       all_protocols = false;
-                       socket_types[0].enabled = true;
-                       break;
-               default:
-                       error(1, 0, "Failed to parse parameters.");
-               }
-       }
-
-       for (s = 0; s < ARRAY_SIZE(socket_types); s++) {
-               if (!all_protocols && !socket_types[s].enabled)
-                       continue;
-
-               printf("Testing %s...\n", socket_types[s].friendly_name);
-               for (t = 0; t < ARRAY_SIZE(test_cases); t++) {
-                       if (!all_tests && !test_cases[t].enabled)
-                               continue;
-
-                       printf("Starting testcase %d...\n", t);
-                       if (run_test_case(socket_types[s], test_cases[t])) {
-                               failures++;
-                               printf("FAILURE in test case ");
-                               print_test_case(&test_cases[t]);
-                       }
-               }
-       }
-       if (!failures)
-               printf("PASSED.\n");
-       return failures;
-}
diff --git a/tools/testing/selftests/networking/timestamping/timestamping.c b/tools/testing/selftests/networking/timestamping/timestamping.c
deleted file mode 100644 (file)
index aca3491..0000000
+++ /dev/null
@@ -1,509 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * This program demonstrates how the various time stamping features in
- * the Linux kernel work. It emulates the behavior of a PTP
- * implementation in stand-alone master mode by sending PTPv1 Sync
- * multicasts once every second. It looks for similar packets, but
- * beyond that doesn't actually implement PTP.
- *
- * Outgoing packets are time stamped with SO_TIMESTAMPING with or
- * without hardware support.
- *
- * Incoming packets are time stamped with SO_TIMESTAMPING with or
- * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
- * SO_TIMESTAMP[NS].
- *
- * Copyright (C) 2009 Intel Corporation.
- * Author: Patrick Ohly <patrick.ohly@intel.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-
-#include <asm/types.h>
-#include <linux/net_tstamp.h>
-#include <linux/errqueue.h>
-#include <linux/sockios.h>
-
-#ifndef SO_TIMESTAMPING
-# define SO_TIMESTAMPING         37
-# define SCM_TIMESTAMPING        SO_TIMESTAMPING
-#endif
-
-#ifndef SO_TIMESTAMPNS
-# define SO_TIMESTAMPNS 35
-#endif
-
-static void usage(const char *error)
-{
-       if (error)
-               printf("invalid option: %s\n", error);
-       printf("timestamping interface option*\n\n"
-              "Options:\n"
-              "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
-              "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
-              "  SO_TIMESTAMPNS - more accurate software time stamping\n"
-              "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
-              "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
-              "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
-              "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
-              "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
-              "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
-              "  SIOCGSTAMP - check last socket time stamp\n"
-              "  SIOCGSTAMPNS - more accurate socket time stamp\n");
-       exit(1);
-}
-
-static void bail(const char *error)
-{
-       printf("%s: %s\n", error, strerror(errno));
-       exit(1);
-}
-
-static const unsigned char sync[] = {
-       0x00, 0x01, 0x00, 0x01,
-       0x5f, 0x44, 0x46, 0x4c,
-       0x54, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x01, 0x01,
-
-       /* fake uuid */
-       0x00, 0x01,
-       0x02, 0x03, 0x04, 0x05,
-
-       0x00, 0x01, 0x00, 0x37,
-       0x00, 0x00, 0x00, 0x08,
-       0x00, 0x00, 0x00, 0x00,
-       0x49, 0x05, 0xcd, 0x01,
-       0x29, 0xb1, 0x8d, 0xb0,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x01,
-
-       /* fake uuid */
-       0x00, 0x01,
-       0x02, 0x03, 0x04, 0x05,
-
-       0x00, 0x00, 0x00, 0x37,
-       0x00, 0x00, 0x00, 0x04,
-       0x44, 0x46, 0x4c, 0x54,
-       0x00, 0x00, 0xf0, 0x60,
-       0x00, 0x01, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0xf0, 0x60,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x04,
-       0x44, 0x46, 0x4c, 0x54,
-       0x00, 0x01,
-
-       /* fake uuid */
-       0x00, 0x01,
-       0x02, 0x03, 0x04, 0x05,
-
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00
-};
-
-static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
-{
-       struct timeval now;
-       int res;
-
-       res = sendto(sock, sync, sizeof(sync), 0,
-               addr, addr_len);
-       gettimeofday(&now, 0);
-       if (res < 0)
-               printf("%s: %s\n", "send", strerror(errno));
-       else
-               printf("%ld.%06ld: sent %d bytes\n",
-                      (long)now.tv_sec, (long)now.tv_usec,
-                      res);
-}
-
-static void printpacket(struct msghdr *msg, int res,
-                       char *data,
-                       int sock, int recvmsg_flags,
-                       int siocgstamp, int siocgstampns)
-{
-       struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
-       struct cmsghdr *cmsg;
-       struct timeval tv;
-       struct timespec ts;
-       struct timeval now;
-
-       gettimeofday(&now, 0);
-
-       printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
-              (long)now.tv_sec, (long)now.tv_usec,
-              (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
-              res,
-              inet_ntoa(from_addr->sin_addr),
-              msg->msg_controllen);
-       for (cmsg = CMSG_FIRSTHDR(msg);
-            cmsg;
-            cmsg = CMSG_NXTHDR(msg, cmsg)) {
-               printf("   cmsg len %zu: ", cmsg->cmsg_len);
-               switch (cmsg->cmsg_level) {
-               case SOL_SOCKET:
-                       printf("SOL_SOCKET ");
-                       switch (cmsg->cmsg_type) {
-                       case SO_TIMESTAMP: {
-                               struct timeval *stamp =
-                                       (struct timeval *)CMSG_DATA(cmsg);
-                               printf("SO_TIMESTAMP %ld.%06ld",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_usec);
-                               break;
-                       }
-                       case SO_TIMESTAMPNS: {
-                               struct timespec *stamp =
-                                       (struct timespec *)CMSG_DATA(cmsg);
-                               printf("SO_TIMESTAMPNS %ld.%09ld",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_nsec);
-                               break;
-                       }
-                       case SO_TIMESTAMPING: {
-                               struct timespec *stamp =
-                                       (struct timespec *)CMSG_DATA(cmsg);
-                               printf("SO_TIMESTAMPING ");
-                               printf("SW %ld.%09ld ",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_nsec);
-                               stamp++;
-                               /* skip deprecated HW transformed */
-                               stamp++;
-                               printf("HW raw %ld.%09ld",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_nsec);
-                               break;
-                       }
-                       default:
-                               printf("type %d", cmsg->cmsg_type);
-                               break;
-                       }
-                       break;
-               case IPPROTO_IP:
-                       printf("IPPROTO_IP ");
-                       switch (cmsg->cmsg_type) {
-                       case IP_RECVERR: {
-                               struct sock_extended_err *err =
-                                       (struct sock_extended_err *)CMSG_DATA(cmsg);
-                               printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
-                                       strerror(err->ee_errno),
-                                       err->ee_origin,
-#ifdef SO_EE_ORIGIN_TIMESTAMPING
-                                       err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
-                                       "bounced packet" : "unexpected origin"
-#else
-                                       "probably SO_EE_ORIGIN_TIMESTAMPING"
-#endif
-                                       );
-                               if (res < sizeof(sync))
-                                       printf(" => truncated data?!");
-                               else if (!memcmp(sync, data + res - sizeof(sync),
-                                                       sizeof(sync)))
-                                       printf(" => GOT OUR DATA BACK (HURRAY!)");
-                               break;
-                       }
-                       case IP_PKTINFO: {
-                               struct in_pktinfo *pktinfo =
-                                       (struct in_pktinfo *)CMSG_DATA(cmsg);
-                               printf("IP_PKTINFO interface index %u",
-                                       pktinfo->ipi_ifindex);
-                               break;
-                       }
-                       default:
-                               printf("type %d", cmsg->cmsg_type);
-                               break;
-                       }
-                       break;
-               default:
-                       printf("level %d type %d",
-                               cmsg->cmsg_level,
-                               cmsg->cmsg_type);
-                       break;
-               }
-               printf("\n");
-       }
-
-       if (siocgstamp) {
-               if (ioctl(sock, SIOCGSTAMP, &tv))
-                       printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
-               else
-                       printf("SIOCGSTAMP %ld.%06ld\n",
-                              (long)tv.tv_sec,
-                              (long)tv.tv_usec);
-       }
-       if (siocgstampns) {
-               if (ioctl(sock, SIOCGSTAMPNS, &ts))
-                       printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
-               else
-                       printf("SIOCGSTAMPNS %ld.%09ld\n",
-                              (long)ts.tv_sec,
-                              (long)ts.tv_nsec);
-       }
-}
-
-static void recvpacket(int sock, int recvmsg_flags,
-                      int siocgstamp, int siocgstampns)
-{
-       char data[256];
-       struct msghdr msg;
-       struct iovec entry;
-       struct sockaddr_in from_addr;
-       struct {
-               struct cmsghdr cm;
-               char control[512];
-       } control;
-       int res;
-
-       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 = (caddr_t)&from_addr;
-       msg.msg_namelen = sizeof(from_addr);
-       msg.msg_control = &control;
-       msg.msg_controllen = sizeof(control);
-
-       res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
-       if (res < 0) {
-               printf("%s %s: %s\n",
-                      "recvmsg",
-                      (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
-                      strerror(errno));
-       } else {
-               printpacket(&msg, res, data,
-                           sock, recvmsg_flags,
-                           siocgstamp, siocgstampns);
-       }
-}
-
-int main(int argc, char **argv)
-{
-       int so_timestamping_flags = 0;
-       int so_timestamp = 0;
-       int so_timestampns = 0;
-       int siocgstamp = 0;
-       int siocgstampns = 0;
-       int ip_multicast_loop = 0;
-       char *interface;
-       int i;
-       int enabled = 1;
-       int sock;
-       struct ifreq device;
-       struct ifreq hwtstamp;
-       struct hwtstamp_config hwconfig, hwconfig_requested;
-       struct sockaddr_in addr;
-       struct ip_mreq imr;
-       struct in_addr iaddr;
-       int val;
-       socklen_t len;
-       struct timeval next;
-
-       if (argc < 2)
-               usage(0);
-       interface = argv[1];
-
-       for (i = 2; i < argc; i++) {
-               if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
-                       so_timestamp = 1;
-               else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
-                       so_timestampns = 1;
-               else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
-                       siocgstamp = 1;
-               else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
-                       siocgstampns = 1;
-               else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
-                       ip_multicast_loop = 1;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
-               else
-                       usage(argv[i]);
-       }
-
-       sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       if (sock < 0)
-               bail("socket");
-
-       memset(&device, 0, sizeof(device));
-       strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
-       if (ioctl(sock, SIOCGIFADDR, &device) < 0)
-               bail("getting interface IP address");
-
-       memset(&hwtstamp, 0, sizeof(hwtstamp));
-       strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
-       hwtstamp.ifr_data = (void *)&hwconfig;
-       memset(&hwconfig, 0, sizeof(hwconfig));
-       hwconfig.tx_type =
-               (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
-               HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       hwconfig.rx_filter =
-               (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
-               HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
-       hwconfig_requested = hwconfig;
-       if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
-               if ((errno == EINVAL || errno == ENOTSUP) &&
-                   hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
-                   hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
-                       printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
-               else
-                       bail("SIOCSHWTSTAMP");
-       }
-       printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
-              hwconfig_requested.tx_type, hwconfig.tx_type,
-              hwconfig_requested.rx_filter, hwconfig.rx_filter);
-
-       /* bind to PTP port */
-       addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-       addr.sin_port = htons(319 /* PTP event port */);
-       if (bind(sock,
-                (struct sockaddr *)&addr,
-                sizeof(struct sockaddr_in)) < 0)
-               bail("bind");
-
-       /* set multicast group for outgoing packets */
-       inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
-       addr.sin_addr = iaddr;
-       imr.imr_multiaddr.s_addr = iaddr.s_addr;
-       imr.imr_interface.s_addr =
-               ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
-       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
-                      &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
-               bail("set multicast");
-
-       /* join multicast group, loop our own packet */
-       if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                      &imr, sizeof(struct ip_mreq)) < 0)
-               bail("join multicast group");
-
-       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
-                      &ip_multicast_loop, sizeof(enabled)) < 0) {
-               bail("loop multicast");
-       }
-
-       /* set socket options for time stamping */
-       if (so_timestamp &&
-               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
-                          &enabled, sizeof(enabled)) < 0)
-               bail("setsockopt SO_TIMESTAMP");
-
-       if (so_timestampns &&
-               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
-                          &enabled, sizeof(enabled)) < 0)
-               bail("setsockopt SO_TIMESTAMPNS");
-
-       if (so_timestamping_flags &&
-               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
-                          &so_timestamping_flags,
-                          sizeof(so_timestamping_flags)) < 0)
-               bail("setsockopt SO_TIMESTAMPING");
-
-       /* request IP_PKTINFO for debugging purposes */
-       if (setsockopt(sock, SOL_IP, IP_PKTINFO,
-                      &enabled, sizeof(enabled)) < 0)
-               printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
-
-       /* verify socket options */
-       len = sizeof(val);
-       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
-               printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
-       else
-               printf("SO_TIMESTAMP %d\n", val);
-
-       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
-               printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
-                      strerror(errno));
-       else
-               printf("SO_TIMESTAMPNS %d\n", val);
-
-       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
-               printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
-                      strerror(errno));
-       } else {
-               printf("SO_TIMESTAMPING %d\n", val);
-               if (val != so_timestamping_flags)
-                       printf("   not the expected value %d\n",
-                              so_timestamping_flags);
-       }
-
-       /* send packets forever every five seconds */
-       gettimeofday(&next, 0);
-       next.tv_sec = (next.tv_sec + 1) / 5 * 5;
-       next.tv_usec = 0;
-       while (1) {
-               struct timeval now;
-               struct timeval delta;
-               long delta_us;
-               int res;
-               fd_set readfs, errorfs;
-
-               gettimeofday(&now, 0);
-               delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
-                       (long)(next.tv_usec - now.tv_usec);
-               if (delta_us > 0) {
-                       /* continue waiting for timeout or data */
-                       delta.tv_sec = delta_us / 1000000;
-                       delta.tv_usec = delta_us % 1000000;
-
-                       FD_ZERO(&readfs);
-                       FD_ZERO(&errorfs);
-                       FD_SET(sock, &readfs);
-                       FD_SET(sock, &errorfs);
-                       printf("%ld.%06ld: select %ldus\n",
-                              (long)now.tv_sec, (long)now.tv_usec,
-                              delta_us);
-                       res = select(sock + 1, &readfs, 0, &errorfs, &delta);
-                       gettimeofday(&now, 0);
-                       printf("%ld.%06ld: select returned: %d, %s\n",
-                              (long)now.tv_sec, (long)now.tv_usec,
-                              res,
-                              res < 0 ? strerror(errno) : "success");
-                       if (res > 0) {
-                               if (FD_ISSET(sock, &readfs))
-                                       printf("ready for reading\n");
-                               if (FD_ISSET(sock, &errorfs))
-                                       printf("has error\n");
-                               recvpacket(sock, 0,
-                                          siocgstamp,
-                                          siocgstampns);
-                               recvpacket(sock, MSG_ERRQUEUE,
-                                          siocgstamp,
-                                          siocgstampns);
-                       }
-               } else {
-                       /* write one packet */
-                       sendpacket(sock,
-                                  (struct sockaddr *)&addr,
-                                  sizeof(addr));
-                       next.tv_sec += 5;
-                       continue;
-               }
-       }
-
-       return 0;
-}
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c
deleted file mode 100644 (file)
index 011b0da..0000000
+++ /dev/null
@@ -1,916 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2014 Google Inc.
- * Author: willemb@google.com (Willem de Bruijn)
- *
- * Test software tx timestamping, including
- *
- * - SCHED, SND and ACK timestamps
- * - RAW, UDP and TCP
- * - IPv4 and IPv6
- * - various packet sizes (to test GSO and TSO)
- *
- * Consult the command line arguments for help on running
- * the various testcases.
- *
- * This test requires a dummy TCP server.
- * A simple `nc6 [-u] -l -p $DESTPORT` will do
- */
-
-#define _GNU_SOURCE
-
-#include <arpa/inet.h>
-#include <asm/types.h>
-#include <error.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <linux/errqueue.h>
-#include <linux/if_ether.h>
-#include <linux/ipv6.h>
-#include <linux/net_tstamp.h>
-#include <netdb.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <netpacket/packet.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/ioctl.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#define NSEC_PER_USEC  1000L
-#define USEC_PER_SEC   1000000L
-#define NSEC_PER_SEC   1000000000LL
-
-/* command line parameters */
-static int cfg_proto = SOCK_STREAM;
-static int cfg_ipproto = IPPROTO_TCP;
-static int cfg_num_pkts = 4;
-static int do_ipv4 = 1;
-static int do_ipv6 = 1;
-static int cfg_payload_len = 10;
-static int cfg_poll_timeout = 100;
-static int cfg_delay_snd;
-static int cfg_delay_ack;
-static bool cfg_show_payload;
-static bool cfg_do_pktinfo;
-static bool cfg_busy_poll;
-static int cfg_sleep_usec = 50 * 1000;
-static bool cfg_loop_nodata;
-static bool cfg_use_cmsg;
-static bool cfg_use_pf_packet;
-static bool cfg_use_epoll;
-static bool cfg_epollet;
-static bool cfg_do_listen;
-static uint16_t dest_port = 9000;
-static bool cfg_print_nsec;
-
-static struct sockaddr_in daddr;
-static struct sockaddr_in6 daddr6;
-static struct timespec ts_usr;
-
-static int saved_tskey = -1;
-static int saved_tskey_type = -1;
-
-struct timing_event {
-       int64_t min;
-       int64_t max;
-       int64_t total;
-       int count;
-};
-
-static struct timing_event usr_enq;
-static struct timing_event usr_snd;
-static struct timing_event usr_ack;
-
-static bool test_failed;
-
-static int64_t timespec_to_ns64(struct timespec *ts)
-{
-       return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
-}
-
-static int64_t timespec_to_us64(struct timespec *ts)
-{
-       return ts->tv_sec * USEC_PER_SEC + ts->tv_nsec / NSEC_PER_USEC;
-}
-
-static void init_timing_event(struct timing_event *te)
-{
-       te->min = INT64_MAX;
-       te->max = 0;
-       te->total = 0;
-       te->count = 0;
-}
-
-static void add_timing_event(struct timing_event *te,
-               struct timespec *t_start, struct timespec *t_end)
-{
-       int64_t ts_delta = timespec_to_ns64(t_end) - timespec_to_ns64(t_start);
-
-       te->count++;
-       if (ts_delta < te->min)
-               te->min = ts_delta;
-       if (ts_delta > te->max)
-               te->max = ts_delta;
-       te->total += ts_delta;
-}
-
-static void validate_key(int tskey, int tstype)
-{
-       int stepsize;
-
-       /* compare key for each subsequent request
-        * must only test for one type, the first one requested
-        */
-       if (saved_tskey == -1)
-               saved_tskey_type = tstype;
-       else if (saved_tskey_type != tstype)
-               return;
-
-       stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1;
-       if (tskey != saved_tskey + stepsize) {
-               fprintf(stderr, "ERROR: key %d, expected %d\n",
-                               tskey, saved_tskey + stepsize);
-               test_failed = true;
-       }
-
-       saved_tskey = tskey;
-}
-
-static void validate_timestamp(struct timespec *cur, int min_delay)
-{
-       int max_delay = min_delay + 500 /* processing time upper bound */;
-       int64_t cur64, start64;
-
-       cur64 = timespec_to_us64(cur);
-       start64 = timespec_to_us64(&ts_usr);
-
-       if (cur64 < start64 + min_delay || cur64 > start64 + max_delay) {
-               fprintf(stderr, "ERROR: %lu us expected between %d and %d\n",
-                               cur64 - start64, min_delay, max_delay);
-               test_failed = true;
-       }
-}
-
-static void __print_ts_delta_formatted(int64_t ts_delta)
-{
-       if (cfg_print_nsec)
-               fprintf(stderr, "%lu ns", ts_delta);
-       else
-               fprintf(stderr, "%lu us", ts_delta / NSEC_PER_USEC);
-}
-
-static void __print_timestamp(const char *name, struct timespec *cur,
-                             uint32_t key, int payload_len)
-{
-       int64_t ts_delta;
-
-       if (!(cur->tv_sec | cur->tv_nsec))
-               return;
-
-       if (cfg_print_nsec)
-               fprintf(stderr, "  %s: %lu s %lu ns (seq=%u, len=%u)",
-                               name, cur->tv_sec, cur->tv_nsec,
-                               key, payload_len);
-       else
-               fprintf(stderr, "  %s: %lu s %lu us (seq=%u, len=%u)",
-                               name, cur->tv_sec, cur->tv_nsec / NSEC_PER_USEC,
-                               key, payload_len);
-
-       if (cur != &ts_usr) {
-               ts_delta = timespec_to_ns64(cur) - timespec_to_ns64(&ts_usr);
-               fprintf(stderr, "  (USR +");
-               __print_ts_delta_formatted(ts_delta);
-               fprintf(stderr, ")");
-       }
-
-       fprintf(stderr, "\n");
-}
-
-static void print_timestamp_usr(void)
-{
-       if (clock_gettime(CLOCK_REALTIME, &ts_usr))
-               error(1, errno, "clock_gettime");
-
-       __print_timestamp("  USR", &ts_usr, 0, 0);
-}
-
-static void print_timestamp(struct scm_timestamping *tss, int tstype,
-                           int tskey, int payload_len)
-{
-       const char *tsname;
-
-       validate_key(tskey, tstype);
-
-       switch (tstype) {
-       case SCM_TSTAMP_SCHED:
-               tsname = "  ENQ";
-               validate_timestamp(&tss->ts[0], 0);
-               add_timing_event(&usr_enq, &ts_usr, &tss->ts[0]);
-               break;
-       case SCM_TSTAMP_SND:
-               tsname = "  SND";
-               validate_timestamp(&tss->ts[0], cfg_delay_snd);
-               add_timing_event(&usr_snd, &ts_usr, &tss->ts[0]);
-               break;
-       case SCM_TSTAMP_ACK:
-               tsname = "  ACK";
-               validate_timestamp(&tss->ts[0], cfg_delay_ack);
-               add_timing_event(&usr_ack, &ts_usr, &tss->ts[0]);
-               break;
-       default:
-               error(1, 0, "unknown timestamp type: %u",
-               tstype);
-       }
-       __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
-}
-
-static void print_timing_event(char *name, struct timing_event *te)
-{
-       if (!te->count)
-               return;
-
-       fprintf(stderr, "    %s: count=%d", name, te->count);
-       fprintf(stderr, ", avg=");
-       __print_ts_delta_formatted((int64_t)(te->total / te->count));
-       fprintf(stderr, ", min=");
-       __print_ts_delta_formatted(te->min);
-       fprintf(stderr, ", max=");
-       __print_ts_delta_formatted(te->max);
-       fprintf(stderr, "\n");
-}
-
-/* TODO: convert to check_and_print payload once API is stable */
-static void print_payload(char *data, int len)
-{
-       int i;
-
-       if (!len)
-               return;
-
-       if (len > 70)
-               len = 70;
-
-       fprintf(stderr, "payload: ");
-       for (i = 0; i < len; i++)
-               fprintf(stderr, "%02hhx ", data[i]);
-       fprintf(stderr, "\n");
-}
-
-static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
-{
-       char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
-
-       fprintf(stderr, "         pktinfo: ifindex=%u src=%s dst=%s\n",
-               ifindex,
-               saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
-               daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
-}
-
-static void __epoll(int epfd)
-{
-       struct epoll_event events;
-       int ret;
-
-       memset(&events, 0, sizeof(events));
-       ret = epoll_wait(epfd, &events, 1, cfg_poll_timeout);
-       if (ret != 1)
-               error(1, errno, "epoll_wait");
-}
-
-static void __poll(int fd)
-{
-       struct pollfd pollfd;
-       int ret;
-
-       memset(&pollfd, 0, sizeof(pollfd));
-       pollfd.fd = fd;
-       ret = poll(&pollfd, 1, cfg_poll_timeout);
-       if (ret != 1)
-               error(1, errno, "poll");
-}
-
-static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
-{
-       struct sock_extended_err *serr = NULL;
-       struct scm_timestamping *tss = NULL;
-       struct cmsghdr *cm;
-       int batch = 0;
-
-       for (cm = CMSG_FIRSTHDR(msg);
-            cm && cm->cmsg_len;
-            cm = CMSG_NXTHDR(msg, cm)) {
-               if (cm->cmsg_level == SOL_SOCKET &&
-                   cm->cmsg_type == SCM_TIMESTAMPING) {
-                       tss = (void *) CMSG_DATA(cm);
-               } else if ((cm->cmsg_level == SOL_IP &&
-                           cm->cmsg_type == IP_RECVERR) ||
-                          (cm->cmsg_level == SOL_IPV6 &&
-                           cm->cmsg_type == IPV6_RECVERR) ||
-                          (cm->cmsg_level == SOL_PACKET &&
-                           cm->cmsg_type == PACKET_TX_TIMESTAMP)) {
-                       serr = (void *) CMSG_DATA(cm);
-                       if (serr->ee_errno != ENOMSG ||
-                           serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
-                               fprintf(stderr, "unknown ip error %d %d\n",
-                                               serr->ee_errno,
-                                               serr->ee_origin);
-                               serr = NULL;
-                       }
-               } else if (cm->cmsg_level == SOL_IP &&
-                          cm->cmsg_type == IP_PKTINFO) {
-                       struct in_pktinfo *info = (void *) CMSG_DATA(cm);
-                       print_pktinfo(AF_INET, info->ipi_ifindex,
-                                     &info->ipi_spec_dst, &info->ipi_addr);
-               } else if (cm->cmsg_level == SOL_IPV6 &&
-                          cm->cmsg_type == IPV6_PKTINFO) {
-                       struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
-                       print_pktinfo(AF_INET6, info6->ipi6_ifindex,
-                                     NULL, &info6->ipi6_addr);
-               } else
-                       fprintf(stderr, "unknown cmsg %d,%d\n",
-                                       cm->cmsg_level, cm->cmsg_type);
-
-               if (serr && tss) {
-                       print_timestamp(tss, serr->ee_info, serr->ee_data,
-                                       payload_len);
-                       serr = NULL;
-                       tss = NULL;
-                       batch++;
-               }
-       }
-
-       if (batch > 1)
-               fprintf(stderr, "batched %d timestamps\n", batch);
-}
-
-static int recv_errmsg(int fd)
-{
-       static char ctrl[1024 /* overprovision*/];
-       static struct msghdr msg;
-       struct iovec entry;
-       static char *data;
-       int ret = 0;
-
-       data = malloc(cfg_payload_len);
-       if (!data)
-               error(1, 0, "malloc");
-
-       memset(&msg, 0, sizeof(msg));
-       memset(&entry, 0, sizeof(entry));
-       memset(ctrl, 0, sizeof(ctrl));
-
-       entry.iov_base = data;
-       entry.iov_len = cfg_payload_len;
-       msg.msg_iov = &entry;
-       msg.msg_iovlen = 1;
-       msg.msg_name = NULL;
-       msg.msg_namelen = 0;
-       msg.msg_control = ctrl;
-       msg.msg_controllen = sizeof(ctrl);
-
-       ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
-       if (ret == -1 && errno != EAGAIN)
-               error(1, errno, "recvmsg");
-
-       if (ret >= 0) {
-               __recv_errmsg_cmsg(&msg, ret);
-               if (cfg_show_payload)
-                       print_payload(data, cfg_payload_len);
-       }
-
-       free(data);
-       return ret == -1;
-}
-
-static uint16_t get_ip_csum(const uint16_t *start, int num_words,
-                           unsigned long sum)
-{
-       int i;
-
-       for (i = 0; i < num_words; i++)
-               sum += start[i];
-
-       while (sum >> 16)
-               sum = (sum & 0xFFFF) + (sum >> 16);
-
-       return ~sum;
-}
-
-static uint16_t get_udp_csum(const struct udphdr *udph, int alen)
-{
-       unsigned long pseudo_sum, csum_len;
-       const void *csum_start = udph;
-
-       pseudo_sum = htons(IPPROTO_UDP);
-       pseudo_sum += udph->len;
-
-       /* checksum ip(v6) addresses + udp header + payload */
-       csum_start -= alen * 2;
-       csum_len = ntohs(udph->len) + alen * 2;
-
-       return get_ip_csum(csum_start, csum_len >> 1, pseudo_sum);
-}
-
-static int fill_header_ipv4(void *p)
-{
-       struct iphdr *iph = p;
-
-       memset(iph, 0, sizeof(*iph));
-
-       iph->ihl        = 5;
-       iph->version    = 4;
-       iph->ttl        = 2;
-       iph->saddr      = daddr.sin_addr.s_addr;        /* set for udp csum calc */
-       iph->daddr      = daddr.sin_addr.s_addr;
-       iph->protocol   = IPPROTO_UDP;
-
-       /* kernel writes saddr, csum, len */
-
-       return sizeof(*iph);
-}
-
-static int fill_header_ipv6(void *p)
-{
-       struct ipv6hdr *ip6h = p;
-
-       memset(ip6h, 0, sizeof(*ip6h));
-
-       ip6h->version           = 6;
-       ip6h->payload_len       = htons(sizeof(struct udphdr) + cfg_payload_len);
-       ip6h->nexthdr           = IPPROTO_UDP;
-       ip6h->hop_limit         = 64;
-
-       ip6h->saddr             = daddr6.sin6_addr;
-       ip6h->daddr             = daddr6.sin6_addr;
-
-       /* kernel does not write saddr in case of ipv6 */
-
-       return sizeof(*ip6h);
-}
-
-static void fill_header_udp(void *p, bool is_ipv4)
-{
-       struct udphdr *udph = p;
-
-       udph->source = ntohs(dest_port + 1);    /* spoof */
-       udph->dest   = ntohs(dest_port);
-       udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
-       udph->check  = 0;
-
-       udph->check  = get_udp_csum(udph, is_ipv4 ? sizeof(struct in_addr) :
-                                                   sizeof(struct in6_addr));
-}
-
-static void do_test(int family, unsigned int report_opt)
-{
-       char control[CMSG_SPACE(sizeof(uint32_t))];
-       struct sockaddr_ll laddr;
-       unsigned int sock_opt;
-       struct cmsghdr *cmsg;
-       struct msghdr msg;
-       struct iovec iov;
-       char *buf;
-       int fd, i, val = 1, total_len, epfd = 0;
-
-       init_timing_event(&usr_enq);
-       init_timing_event(&usr_snd);
-       init_timing_event(&usr_ack);
-
-       total_len = cfg_payload_len;
-       if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
-               total_len += sizeof(struct udphdr);
-               if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW)
-                       if (family == PF_INET)
-                               total_len += sizeof(struct iphdr);
-                       else
-                               total_len += sizeof(struct ipv6hdr);
-
-               /* special case, only rawv6_sendmsg:
-                * pass proto in sin6_port if not connected
-                * also see ANK comment in net/ipv4/raw.c
-                */
-               daddr6.sin6_port = htons(cfg_ipproto);
-       }
-
-       buf = malloc(total_len);
-       if (!buf)
-               error(1, 0, "malloc");
-
-       fd = socket(cfg_use_pf_packet ? PF_PACKET : family,
-                   cfg_proto, cfg_ipproto);
-       if (fd < 0)
-               error(1, errno, "socket");
-
-       if (cfg_use_epoll) {
-               struct epoll_event ev;
-
-               memset(&ev, 0, sizeof(ev));
-               ev.data.fd = fd;
-               if (cfg_epollet)
-                       ev.events |= EPOLLET;
-               epfd = epoll_create(1);
-               if (epfd <= 0)
-                       error(1, errno, "epoll_create");
-               if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
-                       error(1, errno, "epoll_ctl");
-       }
-
-       /* reset expected key on each new socket */
-       saved_tskey = -1;
-
-       if (cfg_proto == SOCK_STREAM) {
-               if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
-                              (char*) &val, sizeof(val)))
-                       error(1, 0, "setsockopt no nagle");
-
-               if (family == PF_INET) {
-                       if (connect(fd, (void *) &daddr, sizeof(daddr)))
-                               error(1, errno, "connect ipv4");
-               } else {
-                       if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
-                               error(1, errno, "connect ipv6");
-               }
-       }
-
-       if (cfg_do_pktinfo) {
-               if (family == AF_INET6) {
-                       if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
-                                      &val, sizeof(val)))
-                               error(1, errno, "setsockopt pktinfo ipv6");
-               } else {
-                       if (setsockopt(fd, SOL_IP, IP_PKTINFO,
-                                      &val, sizeof(val)))
-                               error(1, errno, "setsockopt pktinfo ipv4");
-               }
-       }
-
-       sock_opt = SOF_TIMESTAMPING_SOFTWARE |
-                  SOF_TIMESTAMPING_OPT_CMSG |
-                  SOF_TIMESTAMPING_OPT_ID;
-
-       if (!cfg_use_cmsg)
-               sock_opt |= report_opt;
-
-       if (cfg_loop_nodata)
-               sock_opt |= SOF_TIMESTAMPING_OPT_TSONLY;
-
-       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
-                      (char *) &sock_opt, sizeof(sock_opt)))
-               error(1, 0, "setsockopt timestamping");
-
-       for (i = 0; i < cfg_num_pkts; i++) {
-               memset(&msg, 0, sizeof(msg));
-               memset(buf, 'a' + i, total_len);
-
-               if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
-                       int off = 0;
-
-                       if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) {
-                               if (family == PF_INET)
-                                       off = fill_header_ipv4(buf);
-                               else
-                                       off = fill_header_ipv6(buf);
-                       }
-
-                       fill_header_udp(buf + off, family == PF_INET);
-               }
-
-               print_timestamp_usr();
-
-               iov.iov_base = buf;
-               iov.iov_len = total_len;
-
-               if (cfg_proto != SOCK_STREAM) {
-                       if (cfg_use_pf_packet) {
-                               memset(&laddr, 0, sizeof(laddr));
-
-                               laddr.sll_family        = AF_PACKET;
-                               laddr.sll_ifindex       = 1;
-                               laddr.sll_protocol      = htons(family == AF_INET ? ETH_P_IP : ETH_P_IPV6);
-                               laddr.sll_halen         = ETH_ALEN;
-
-                               msg.msg_name = (void *)&laddr;
-                               msg.msg_namelen = sizeof(laddr);
-                       } else if (family == PF_INET) {
-                               msg.msg_name = (void *)&daddr;
-                               msg.msg_namelen = sizeof(daddr);
-                       } else {
-                               msg.msg_name = (void *)&daddr6;
-                               msg.msg_namelen = sizeof(daddr6);
-                       }
-               }
-
-               msg.msg_iov = &iov;
-               msg.msg_iovlen = 1;
-
-               if (cfg_use_cmsg) {
-                       memset(control, 0, sizeof(control));
-
-                       msg.msg_control = control;
-                       msg.msg_controllen = sizeof(control);
-
-                       cmsg = CMSG_FIRSTHDR(&msg);
-                       cmsg->cmsg_level = SOL_SOCKET;
-                       cmsg->cmsg_type = SO_TIMESTAMPING;
-                       cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
-
-                       *((uint32_t *) CMSG_DATA(cmsg)) = report_opt;
-               }
-
-               val = sendmsg(fd, &msg, 0);
-               if (val != total_len)
-                       error(1, errno, "send");
-
-               /* wait for all errors to be queued, else ACKs arrive OOO */
-               if (cfg_sleep_usec)
-                       usleep(cfg_sleep_usec);
-
-               if (!cfg_busy_poll) {
-                       if (cfg_use_epoll)
-                               __epoll(epfd);
-                       else
-                               __poll(fd);
-               }
-
-               while (!recv_errmsg(fd)) {}
-       }
-
-       print_timing_event("USR-ENQ", &usr_enq);
-       print_timing_event("USR-SND", &usr_snd);
-       print_timing_event("USR-ACK", &usr_ack);
-
-       if (close(fd))
-               error(1, errno, "close");
-
-       free(buf);
-       usleep(100 * NSEC_PER_USEC);
-}
-
-static void __attribute__((noreturn)) usage(const char *filepath)
-{
-       fprintf(stderr, "\nUsage: %s [options] hostname\n"
-                       "\nwhere options are:\n"
-                       "  -4:   only IPv4\n"
-                       "  -6:   only IPv6\n"
-                       "  -h:   show this message\n"
-                       "  -b:   busy poll to read from error queue\n"
-                       "  -c N: number of packets for each test\n"
-                       "  -C:   use cmsg to set tstamp recording options\n"
-                       "  -e:   use level-triggered epoll() instead of poll()\n"
-                       "  -E:   use event-triggered epoll() instead of poll()\n"
-                       "  -F:   poll()/epoll() waits forever for an event\n"
-                       "  -I:   request PKTINFO\n"
-                       "  -l N: send N bytes at a time\n"
-                       "  -L    listen on hostname and port\n"
-                       "  -n:   set no-payload option\n"
-                       "  -N:   print timestamps and durations in nsec (instead of usec)\n"
-                       "  -p N: connect to port N\n"
-                       "  -P:   use PF_PACKET\n"
-                       "  -r:   use raw\n"
-                       "  -R:   use raw (IP_HDRINCL)\n"
-                       "  -S N: usec to sleep before reading error queue\n"
-                       "  -u:   use udp\n"
-                       "  -v:   validate SND delay (usec)\n"
-                       "  -V:   validate ACK delay (usec)\n"
-                       "  -x:   show payload (up to 70 bytes)\n",
-                       filepath);
-       exit(1);
-}
-
-static void parse_opt(int argc, char **argv)
-{
-       int proto_count = 0;
-       int c;
-
-       while ((c = getopt(argc, argv,
-                               "46bc:CeEFhIl:LnNp:PrRS:uv:V:x")) != -1) {
-               switch (c) {
-               case '4':
-                       do_ipv6 = 0;
-                       break;
-               case '6':
-                       do_ipv4 = 0;
-                       break;
-               case 'b':
-                       cfg_busy_poll = true;
-                       break;
-               case 'c':
-                       cfg_num_pkts = strtoul(optarg, NULL, 10);
-                       break;
-               case 'C':
-                       cfg_use_cmsg = true;
-                       break;
-               case 'e':
-                       cfg_use_epoll = true;
-                       break;
-               case 'E':
-                       cfg_use_epoll = true;
-                       cfg_epollet = true;
-               case 'F':
-                       cfg_poll_timeout = -1;
-                       break;
-               case 'I':
-                       cfg_do_pktinfo = true;
-                       break;
-               case 'l':
-                       cfg_payload_len = strtoul(optarg, NULL, 10);
-                       break;
-               case 'L':
-                       cfg_do_listen = true;
-                       break;
-               case 'n':
-                       cfg_loop_nodata = true;
-                       break;
-               case 'N':
-                       cfg_print_nsec = true;
-                       break;
-               case 'p':
-                       dest_port = strtoul(optarg, NULL, 10);
-                       break;
-               case 'P':
-                       proto_count++;
-                       cfg_use_pf_packet = true;
-                       cfg_proto = SOCK_DGRAM;
-                       cfg_ipproto = 0;
-                       break;
-               case 'r':
-                       proto_count++;
-                       cfg_proto = SOCK_RAW;
-                       cfg_ipproto = IPPROTO_UDP;
-                       break;
-               case 'R':
-                       proto_count++;
-                       cfg_proto = SOCK_RAW;
-                       cfg_ipproto = IPPROTO_RAW;
-                       break;
-               case 'S':
-                       cfg_sleep_usec = strtoul(optarg, NULL, 10);
-                       break;
-               case 'u':
-                       proto_count++;
-                       cfg_proto = SOCK_DGRAM;
-                       cfg_ipproto = IPPROTO_UDP;
-                       break;
-               case 'v':
-                       cfg_delay_snd = strtoul(optarg, NULL, 10);
-                       break;
-               case 'V':
-                       cfg_delay_ack = strtoul(optarg, NULL, 10);
-                       break;
-               case 'x':
-                       cfg_show_payload = true;
-                       break;
-               case 'h':
-               default:
-                       usage(argv[0]);
-               }
-       }
-
-       if (!cfg_payload_len)
-               error(1, 0, "payload may not be nonzero");
-       if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
-               error(1, 0, "udp packet might exceed expected MTU");
-       if (!do_ipv4 && !do_ipv6)
-               error(1, 0, "pass -4 or -6, not both");
-       if (proto_count > 1)
-               error(1, 0, "pass -P, -r, -R or -u, not multiple");
-       if (cfg_do_pktinfo && cfg_use_pf_packet)
-               error(1, 0, "cannot ask for pktinfo over pf_packet");
-       if (cfg_busy_poll && cfg_use_epoll)
-               error(1, 0, "pass epoll or busy_poll, not both");
-
-       if (optind != argc - 1)
-               error(1, 0, "missing required hostname argument");
-}
-
-static void resolve_hostname(const char *hostname)
-{
-       struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 };
-       struct addrinfo *addrs, *cur;
-       int have_ipv4 = 0, have_ipv6 = 0;
-
-retry:
-       if (getaddrinfo(hostname, NULL, &hints, &addrs))
-               error(1, errno, "getaddrinfo");
-
-       cur = addrs;
-       while (cur && !have_ipv4 && !have_ipv6) {
-               if (!have_ipv4 && cur->ai_family == AF_INET) {
-                       memcpy(&daddr, cur->ai_addr, sizeof(daddr));
-                       daddr.sin_port = htons(dest_port);
-                       have_ipv4 = 1;
-               }
-               else if (!have_ipv6 && cur->ai_family == AF_INET6) {
-                       memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
-                       daddr6.sin6_port = htons(dest_port);
-                       have_ipv6 = 1;
-               }
-               cur = cur->ai_next;
-       }
-       if (addrs)
-               freeaddrinfo(addrs);
-
-       if (do_ipv6 && hints.ai_family != AF_INET6) {
-               hints.ai_family = AF_INET6;
-               goto retry;
-       }
-
-       do_ipv4 &= have_ipv4;
-       do_ipv6 &= have_ipv6;
-}
-
-static void do_listen(int family, void *addr, int alen)
-{
-       int fd, type;
-
-       type = cfg_proto == SOCK_RAW ? SOCK_DGRAM : cfg_proto;
-
-       fd = socket(family, type, 0);
-       if (fd == -1)
-               error(1, errno, "socket rx");
-
-       if (bind(fd, addr, alen))
-               error(1, errno, "bind rx");
-
-       if (type == SOCK_STREAM && listen(fd, 10))
-               error(1, errno, "listen rx");
-
-       /* leave fd open, will be closed on process exit.
-        * this enables connect() to succeed and avoids icmp replies
-        */
-}
-
-static void do_main(int family)
-{
-       fprintf(stderr, "family:       %s %s\n",
-                       family == PF_INET ? "INET" : "INET6",
-                       cfg_use_pf_packet ? "(PF_PACKET)" : "");
-
-       fprintf(stderr, "test SND\n");
-       do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
-
-       fprintf(stderr, "test ENQ\n");
-       do_test(family, SOF_TIMESTAMPING_TX_SCHED);
-
-       fprintf(stderr, "test ENQ + SND\n");
-       do_test(family, SOF_TIMESTAMPING_TX_SCHED |
-                       SOF_TIMESTAMPING_TX_SOFTWARE);
-
-       if (cfg_proto == SOCK_STREAM) {
-               fprintf(stderr, "\ntest ACK\n");
-               do_test(family, SOF_TIMESTAMPING_TX_ACK);
-
-               fprintf(stderr, "\ntest SND + ACK\n");
-               do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
-                               SOF_TIMESTAMPING_TX_ACK);
-
-               fprintf(stderr, "\ntest ENQ + SND + ACK\n");
-               do_test(family, SOF_TIMESTAMPING_TX_SCHED |
-                               SOF_TIMESTAMPING_TX_SOFTWARE |
-                               SOF_TIMESTAMPING_TX_ACK);
-       }
-}
-
-const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
-
-int main(int argc, char **argv)
-{
-       if (argc == 1)
-               usage(argv[0]);
-
-       parse_opt(argc, argv);
-       resolve_hostname(argv[argc - 1]);
-
-       fprintf(stderr, "protocol:     %s\n", sock_names[cfg_proto]);
-       fprintf(stderr, "payload:      %u\n", cfg_payload_len);
-       fprintf(stderr, "server port:  %u\n", dest_port);
-       fprintf(stderr, "\n");
-
-       if (do_ipv4) {
-               if (cfg_do_listen)
-                       do_listen(PF_INET, &daddr, sizeof(daddr));
-               do_main(PF_INET);
-       }
-
-       if (do_ipv6) {
-               if (cfg_do_listen)
-                       do_listen(PF_INET6, &daddr6, sizeof(daddr6));
-               do_main(PF_INET6);
-       }
-
-       return test_failed;
-}
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.sh b/tools/testing/selftests/networking/timestamping/txtimestamp.sh
deleted file mode 100755 (executable)
index 70a8cda..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Send packets with transmit timestamps over loopback with netem
-# Verify that timestamps correspond to netem delay
-
-set -e
-
-setup() {
-       # set 1ms delay on lo egress
-       tc qdisc add dev lo root netem delay 1ms
-
-       # set 2ms delay on ifb0 egress
-       modprobe ifb
-       ip link add ifb_netem0 type ifb
-       ip link set dev ifb_netem0 up
-       tc qdisc add dev ifb_netem0 root netem delay 2ms
-
-       # redirect lo ingress through ifb0 egress
-       tc qdisc add dev lo handle ffff: ingress
-       tc filter add dev lo parent ffff: \
-               u32 match mark 0 0xffff \
-               action mirred egress redirect dev ifb_netem0
-}
-
-run_test_v4v6() {
-       # SND will be delayed 1000us
-       # ACK will be delayed 6000us: 1 + 2 ms round-trip
-       local -r args="$@ -v 1000 -V 6000"
-
-       ./txtimestamp ${args} -4 -L 127.0.0.1
-       ./txtimestamp ${args} -6 -L ::1
-}
-
-run_test_tcpudpraw() {
-       local -r args=$@
-
-       run_test_v4v6 ${args}           # tcp
-       run_test_v4v6 ${args} -u        # udp
-       run_test_v4v6 ${args} -r        # raw
-       run_test_v4v6 ${args} -R        # raw (IPPROTO_RAW)
-       run_test_v4v6 ${args} -P        # pf_packet
-}
-
-run_test_all() {
-       setup
-       run_test_tcpudpraw              # setsockopt
-       run_test_tcpudpraw -C           # cmsg
-       run_test_tcpudpraw -n           # timestamp w/o data
-       echo "OK. All tests passed"
-}
-
-run_test_one() {
-       setup
-       ./txtimestamp $@
-}
-
-usage() {
-       echo "Usage: $0 [ -r | --run ] <txtimestamp args> | [ -h | --help ]"
-       echo "  (no args)  Run all tests"
-       echo "  -r|--run  Run an individual test with arguments"
-       echo "  -h|--help Help"
-}
-
-main() {
-       if [[ $# -eq 0 ]]; then
-               run_test_all
-       else
-               if [[ "$1" = "-r" || "$1" == "--run" ]]; then
-                       shift
-                       run_test_one $@
-               else
-                       usage
-               fi
-       fi
-}
-
-if [[ "$(ip netns identify)" == "root" ]]; then
-       ../../net/in_netns.sh $0 $@
-else
-       main $@
-fi