// 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 int cfg_delay_tolerance_usec = 500;
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)
{
int64_t cur64, start64;
int max_delay;
cur64 = timespec_to_us64(cur);
start64 = timespec_to_us64(&ts_usr);
max_delay = min_delay + cfg_delay_tolerance_usec;
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,