linux/tools/testing/selftests/networking/timestamping/txtimestamp.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014 Google Inc.
   3 * Author: willemb@google.com (Willem de Bruijn)
   4 *
   5 * Test software tx timestamping, including
   6 *
   7 * - SCHED, SND and ACK timestamps
   8 * - RAW, UDP and TCP
   9 * - IPv4 and IPv6
  10 * - various packet sizes (to test GSO and TSO)
  11 *
  12 * Consult the command line arguments for help on running
  13 * the various testcases.
  14 *
  15 * This test requires a dummy TCP server.
  16 * A simple `nc6 [-u] -l -p $DESTPORT` will do
  17 *
  18 *
  19 * This program is free software; you can redistribute it and/or modify it
  20 * under the terms and conditions of the GNU General Public License,
  21 * version 2, as published by the Free Software Foundation.
  22 *
  23 * This program is distributed in the hope it will be useful, but WITHOUT
  24 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  25 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
  26 * more details.
  27 *
  28 * You should have received a copy of the GNU General Public License along with
  29 * this program; if not, write to the Free Software Foundation, Inc.,
  30 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  31 */
  32
  33#define _GNU_SOURCE
  34
  35#include <arpa/inet.h>
  36#include <asm/types.h>
  37#include <error.h>
  38#include <errno.h>
  39#include <inttypes.h>
  40#include <linux/errqueue.h>
  41#include <linux/if_ether.h>
  42#include <linux/net_tstamp.h>
  43#include <netdb.h>
  44#include <net/if.h>
  45#include <netinet/in.h>
  46#include <netinet/ip.h>
  47#include <netinet/udp.h>
  48#include <netinet/tcp.h>
  49#include <netpacket/packet.h>
  50#include <poll.h>
  51#include <stdarg.h>
  52#include <stdbool.h>
  53#include <stdio.h>
  54#include <stdlib.h>
  55#include <string.h>
  56#include <sys/ioctl.h>
  57#include <sys/select.h>
  58#include <sys/socket.h>
  59#include <sys/time.h>
  60#include <sys/types.h>
  61#include <time.h>
  62#include <unistd.h>
  63
  64/* command line parameters */
  65static int cfg_proto = SOCK_STREAM;
  66static int cfg_ipproto = IPPROTO_TCP;
  67static int cfg_num_pkts = 4;
  68static int do_ipv4 = 1;
  69static int do_ipv6 = 1;
  70static int cfg_payload_len = 10;
  71static int cfg_poll_timeout = 100;
  72static bool cfg_show_payload;
  73static bool cfg_do_pktinfo;
  74static bool cfg_loop_nodata;
  75static bool cfg_no_delay;
  76static uint16_t dest_port = 9000;
  77
  78static struct sockaddr_in daddr;
  79static struct sockaddr_in6 daddr6;
  80static struct timespec ts_prev;
  81
  82static void __print_timestamp(const char *name, struct timespec *cur,
  83                              uint32_t key, int payload_len)
  84{
  85        if (!(cur->tv_sec | cur->tv_nsec))
  86                return;
  87
  88        fprintf(stderr, "  %s: %lu s %lu us (seq=%u, len=%u)",
  89                        name, cur->tv_sec, cur->tv_nsec / 1000,
  90                        key, payload_len);
  91
  92        if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
  93                int64_t cur_ms, prev_ms;
  94
  95                cur_ms = (long) cur->tv_sec * 1000 * 1000;
  96                cur_ms += cur->tv_nsec / 1000;
  97
  98                prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
  99                prev_ms += ts_prev.tv_nsec / 1000;
 100
 101                fprintf(stderr, "  (%+" PRId64 " us)", cur_ms - prev_ms);
 102        }
 103
 104        ts_prev = *cur;
 105        fprintf(stderr, "\n");
 106}
 107
 108static void print_timestamp_usr(void)
 109{
 110        struct timespec ts;
 111        struct timeval tv;      /* avoid dependency on -lrt */
 112
 113        gettimeofday(&tv, NULL);
 114        ts.tv_sec = tv.tv_sec;
 115        ts.tv_nsec = tv.tv_usec * 1000;
 116
 117        __print_timestamp("  USR", &ts, 0, 0);
 118}
 119
 120static void print_timestamp(struct scm_timestamping *tss, int tstype,
 121                            int tskey, int payload_len)
 122{
 123        const char *tsname;
 124
 125        switch (tstype) {
 126        case SCM_TSTAMP_SCHED:
 127                tsname = "  ENQ";
 128                break;
 129        case SCM_TSTAMP_SND:
 130                tsname = "  SND";
 131                break;
 132        case SCM_TSTAMP_ACK:
 133                tsname = "  ACK";
 134                break;
 135        default:
 136                error(1, 0, "unknown timestamp type: %u",
 137                tstype);
 138        }
 139        __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
 140}
 141
 142/* TODO: convert to check_and_print payload once API is stable */
 143static void print_payload(char *data, int len)
 144{
 145        int i;
 146
 147        if (!len)
 148                return;
 149
 150        if (len > 70)
 151                len = 70;
 152
 153        fprintf(stderr, "payload: ");
 154        for (i = 0; i < len; i++)
 155                fprintf(stderr, "%02hhx ", data[i]);
 156        fprintf(stderr, "\n");
 157}
 158
 159static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
 160{
 161        char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
 162
 163        fprintf(stderr, "         pktinfo: ifindex=%u src=%s dst=%s\n",
 164                ifindex,
 165                saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
 166                daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
 167}
 168
 169static void __poll(int fd)
 170{
 171        struct pollfd pollfd;
 172        int ret;
 173
 174        memset(&pollfd, 0, sizeof(pollfd));
 175        pollfd.fd = fd;
 176        ret = poll(&pollfd, 1, cfg_poll_timeout);
 177        if (ret != 1)
 178                error(1, errno, "poll");
 179}
 180
 181static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
 182{
 183        struct sock_extended_err *serr = NULL;
 184        struct scm_timestamping *tss = NULL;
 185        struct cmsghdr *cm;
 186        int batch = 0;
 187
 188        for (cm = CMSG_FIRSTHDR(msg);
 189             cm && cm->cmsg_len;
 190             cm = CMSG_NXTHDR(msg, cm)) {
 191                if (cm->cmsg_level == SOL_SOCKET &&
 192                    cm->cmsg_type == SCM_TIMESTAMPING) {
 193                        tss = (void *) CMSG_DATA(cm);
 194                } else if ((cm->cmsg_level == SOL_IP &&
 195                            cm->cmsg_type == IP_RECVERR) ||
 196                           (cm->cmsg_level == SOL_IPV6 &&
 197                            cm->cmsg_type == IPV6_RECVERR)) {
 198                        serr = (void *) CMSG_DATA(cm);
 199                        if (serr->ee_errno != ENOMSG ||
 200                            serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
 201                                fprintf(stderr, "unknown ip error %d %d\n",
 202                                                serr->ee_errno,
 203                                                serr->ee_origin);
 204                                serr = NULL;
 205                        }
 206                } else if (cm->cmsg_level == SOL_IP &&
 207                           cm->cmsg_type == IP_PKTINFO) {
 208                        struct in_pktinfo *info = (void *) CMSG_DATA(cm);
 209                        print_pktinfo(AF_INET, info->ipi_ifindex,
 210                                      &info->ipi_spec_dst, &info->ipi_addr);
 211                } else if (cm->cmsg_level == SOL_IPV6 &&
 212                           cm->cmsg_type == IPV6_PKTINFO) {
 213                        struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
 214                        print_pktinfo(AF_INET6, info6->ipi6_ifindex,
 215                                      NULL, &info6->ipi6_addr);
 216                } else
 217                        fprintf(stderr, "unknown cmsg %d,%d\n",
 218                                        cm->cmsg_level, cm->cmsg_type);
 219
 220                if (serr && tss) {
 221                        print_timestamp(tss, serr->ee_info, serr->ee_data,
 222                                        payload_len);
 223                        serr = NULL;
 224                        tss = NULL;
 225                        batch++;
 226                }
 227        }
 228
 229        if (batch > 1)
 230                fprintf(stderr, "batched %d timestamps\n", batch);
 231}
 232
 233static int recv_errmsg(int fd)
 234{
 235        static char ctrl[1024 /* overprovision*/];
 236        static struct msghdr msg;
 237        struct iovec entry;
 238        static char *data;
 239        int ret = 0;
 240
 241        data = malloc(cfg_payload_len);
 242        if (!data)
 243                error(1, 0, "malloc");
 244
 245        memset(&msg, 0, sizeof(msg));
 246        memset(&entry, 0, sizeof(entry));
 247        memset(ctrl, 0, sizeof(ctrl));
 248
 249        entry.iov_base = data;
 250        entry.iov_len = cfg_payload_len;
 251        msg.msg_iov = &entry;
 252        msg.msg_iovlen = 1;
 253        msg.msg_name = NULL;
 254        msg.msg_namelen = 0;
 255        msg.msg_control = ctrl;
 256        msg.msg_controllen = sizeof(ctrl);
 257
 258        ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
 259        if (ret == -1 && errno != EAGAIN)
 260                error(1, errno, "recvmsg");
 261
 262        if (ret >= 0) {
 263                __recv_errmsg_cmsg(&msg, ret);
 264                if (cfg_show_payload)
 265                        print_payload(data, cfg_payload_len);
 266        }
 267
 268        free(data);
 269        return ret == -1;
 270}
 271
 272static void do_test(int family, unsigned int opt)
 273{
 274        char *buf;
 275        int fd, i, val = 1, total_len;
 276
 277        if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
 278                /* due to lack of checksum generation code */
 279                fprintf(stderr, "test: skipping datagram over IPv6\n");
 280                return;
 281        }
 282
 283        total_len = cfg_payload_len;
 284        if (cfg_proto == SOCK_RAW) {
 285                total_len += sizeof(struct udphdr);
 286                if (cfg_ipproto == IPPROTO_RAW)
 287                        total_len += sizeof(struct iphdr);
 288        }
 289
 290        buf = malloc(total_len);
 291        if (!buf)
 292                error(1, 0, "malloc");
 293
 294        fd = socket(family, cfg_proto, cfg_ipproto);
 295        if (fd < 0)
 296                error(1, errno, "socket");
 297
 298        if (cfg_proto == SOCK_STREAM) {
 299                if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
 300                               (char*) &val, sizeof(val)))
 301                        error(1, 0, "setsockopt no nagle");
 302
 303                if (family == PF_INET) {
 304                        if (connect(fd, (void *) &daddr, sizeof(daddr)))
 305                                error(1, errno, "connect ipv4");
 306                } else {
 307                        if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
 308                                error(1, errno, "connect ipv6");
 309                }
 310        }
 311
 312        if (cfg_do_pktinfo) {
 313                if (family == AF_INET6) {
 314                        if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
 315                                       &val, sizeof(val)))
 316                                error(1, errno, "setsockopt pktinfo ipv6");
 317                } else {
 318                        if (setsockopt(fd, SOL_IP, IP_PKTINFO,
 319                                       &val, sizeof(val)))
 320                                error(1, errno, "setsockopt pktinfo ipv4");
 321                }
 322        }
 323
 324        opt |= SOF_TIMESTAMPING_SOFTWARE |
 325               SOF_TIMESTAMPING_OPT_CMSG |
 326               SOF_TIMESTAMPING_OPT_ID;
 327        if (cfg_loop_nodata)
 328                opt |= SOF_TIMESTAMPING_OPT_TSONLY;
 329
 330        if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
 331                       (char *) &opt, sizeof(opt)))
 332                error(1, 0, "setsockopt timestamping");
 333
 334        for (i = 0; i < cfg_num_pkts; i++) {
 335                memset(&ts_prev, 0, sizeof(ts_prev));
 336                memset(buf, 'a' + i, total_len);
 337
 338                if (cfg_proto == SOCK_RAW) {
 339                        struct udphdr *udph;
 340                        int off = 0;
 341
 342                        if (cfg_ipproto == IPPROTO_RAW) {
 343                                struct iphdr *iph = (void *) buf;
 344
 345                                memset(iph, 0, sizeof(*iph));
 346                                iph->ihl      = 5;
 347                                iph->version  = 4;
 348                                iph->ttl      = 2;
 349                                iph->daddr    = daddr.sin_addr.s_addr;
 350                                iph->protocol = IPPROTO_UDP;
 351                                /* kernel writes saddr, csum, len */
 352
 353                                off = sizeof(*iph);
 354                        }
 355
 356                        udph = (void *) buf + off;
 357                        udph->source = ntohs(9000);     /* random spoof */
 358                        udph->dest   = ntohs(dest_port);
 359                        udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
 360                        udph->check  = 0;       /* not allowed for IPv6 */
 361                }
 362
 363                print_timestamp_usr();
 364                if (cfg_proto != SOCK_STREAM) {
 365                        if (family == PF_INET)
 366                                val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
 367                        else
 368                                val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
 369                } else {
 370                        val = send(fd, buf, cfg_payload_len, 0);
 371                }
 372                if (val != total_len)
 373                        error(1, errno, "send");
 374
 375                /* wait for all errors to be queued, else ACKs arrive OOO */
 376                if (!cfg_no_delay)
 377                        usleep(50 * 1000);
 378
 379                __poll(fd);
 380
 381                while (!recv_errmsg(fd)) {}
 382        }
 383
 384        if (close(fd))
 385                error(1, errno, "close");
 386
 387        free(buf);
 388        usleep(400 * 1000);
 389}
 390
 391static void __attribute__((noreturn)) usage(const char *filepath)
 392{
 393        fprintf(stderr, "\nUsage: %s [options] hostname\n"
 394                        "\nwhere options are:\n"
 395                        "  -4:   only IPv4\n"
 396                        "  -6:   only IPv6\n"
 397                        "  -h:   show this message\n"
 398                        "  -c N: number of packets for each test\n"
 399                        "  -D:   no delay between packets\n"
 400                        "  -F:   poll() waits forever for an event\n"
 401                        "  -I:   request PKTINFO\n"
 402                        "  -l N: send N bytes at a time\n"
 403                        "  -n:   set no-payload option\n"
 404                        "  -r:   use raw\n"
 405                        "  -R:   use raw (IP_HDRINCL)\n"
 406                        "  -p N: connect to port N\n"
 407                        "  -u:   use udp\n"
 408                        "  -x:   show payload (up to 70 bytes)\n",
 409                        filepath);
 410        exit(1);
 411}
 412
 413static void parse_opt(int argc, char **argv)
 414{
 415        int proto_count = 0;
 416        char c;
 417
 418        while ((c = getopt(argc, argv, "46c:DFhIl:np:rRux")) != -1) {
 419                switch (c) {
 420                case '4':
 421                        do_ipv6 = 0;
 422                        break;
 423                case '6':
 424                        do_ipv4 = 0;
 425                        break;
 426                case 'c':
 427                        cfg_num_pkts = strtoul(optarg, NULL, 10);
 428                        break;
 429                case 'D':
 430                        cfg_no_delay = true;
 431                        break;
 432                case 'F':
 433                        cfg_poll_timeout = -1;
 434                        break;
 435                case 'I':
 436                        cfg_do_pktinfo = true;
 437                        break;
 438                case 'n':
 439                        cfg_loop_nodata = true;
 440                        break;
 441                case 'r':
 442                        proto_count++;
 443                        cfg_proto = SOCK_RAW;
 444                        cfg_ipproto = IPPROTO_UDP;
 445                        break;
 446                case 'R':
 447                        proto_count++;
 448                        cfg_proto = SOCK_RAW;
 449                        cfg_ipproto = IPPROTO_RAW;
 450                        break;
 451                case 'u':
 452                        proto_count++;
 453                        cfg_proto = SOCK_DGRAM;
 454                        cfg_ipproto = IPPROTO_UDP;
 455                        break;
 456                case 'l':
 457                        cfg_payload_len = strtoul(optarg, NULL, 10);
 458                        break;
 459                case 'p':
 460                        dest_port = strtoul(optarg, NULL, 10);
 461                        break;
 462                case 'x':
 463                        cfg_show_payload = true;
 464                        break;
 465                case 'h':
 466                default:
 467                        usage(argv[0]);
 468                }
 469        }
 470
 471        if (!cfg_payload_len)
 472                error(1, 0, "payload may not be nonzero");
 473        if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
 474                error(1, 0, "udp packet might exceed expected MTU");
 475        if (!do_ipv4 && !do_ipv6)
 476                error(1, 0, "pass -4 or -6, not both");
 477        if (proto_count > 1)
 478                error(1, 0, "pass -r, -R or -u, not multiple");
 479
 480        if (optind != argc - 1)
 481                error(1, 0, "missing required hostname argument");
 482}
 483
 484static void resolve_hostname(const char *hostname)
 485{
 486        struct addrinfo *addrs, *cur;
 487        int have_ipv4 = 0, have_ipv6 = 0;
 488
 489        if (getaddrinfo(hostname, NULL, NULL, &addrs))
 490                error(1, errno, "getaddrinfo");
 491
 492        cur = addrs;
 493        while (cur && !have_ipv4 && !have_ipv6) {
 494                if (!have_ipv4 && cur->ai_family == AF_INET) {
 495                        memcpy(&daddr, cur->ai_addr, sizeof(daddr));
 496                        daddr.sin_port = htons(dest_port);
 497                        have_ipv4 = 1;
 498                }
 499                else if (!have_ipv6 && cur->ai_family == AF_INET6) {
 500                        memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
 501                        daddr6.sin6_port = htons(dest_port);
 502                        have_ipv6 = 1;
 503                }
 504                cur = cur->ai_next;
 505        }
 506        if (addrs)
 507                freeaddrinfo(addrs);
 508
 509        do_ipv4 &= have_ipv4;
 510        do_ipv6 &= have_ipv6;
 511}
 512
 513static void do_main(int family)
 514{
 515        fprintf(stderr, "family:       %s\n",
 516                        family == PF_INET ? "INET" : "INET6");
 517
 518        fprintf(stderr, "test SND\n");
 519        do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
 520
 521        fprintf(stderr, "test ENQ\n");
 522        do_test(family, SOF_TIMESTAMPING_TX_SCHED);
 523
 524        fprintf(stderr, "test ENQ + SND\n");
 525        do_test(family, SOF_TIMESTAMPING_TX_SCHED |
 526                        SOF_TIMESTAMPING_TX_SOFTWARE);
 527
 528        if (cfg_proto == SOCK_STREAM) {
 529                fprintf(stderr, "\ntest ACK\n");
 530                do_test(family, SOF_TIMESTAMPING_TX_ACK);
 531
 532                fprintf(stderr, "\ntest SND + ACK\n");
 533                do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
 534                                SOF_TIMESTAMPING_TX_ACK);
 535
 536                fprintf(stderr, "\ntest ENQ + SND + ACK\n");
 537                do_test(family, SOF_TIMESTAMPING_TX_SCHED |
 538                                SOF_TIMESTAMPING_TX_SOFTWARE |
 539                                SOF_TIMESTAMPING_TX_ACK);
 540        }
 541}
 542
 543const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
 544
 545int main(int argc, char **argv)
 546{
 547        if (argc == 1)
 548                usage(argv[0]);
 549
 550        parse_opt(argc, argv);
 551        resolve_hostname(argv[argc - 1]);
 552
 553        fprintf(stderr, "protocol:     %s\n", sock_names[cfg_proto]);
 554        fprintf(stderr, "payload:      %u\n", cfg_payload_len);
 555        fprintf(stderr, "server port:  %u\n", dest_port);
 556        fprintf(stderr, "\n");
 557
 558        if (do_ipv4)
 559                do_main(PF_INET);
 560        if (do_ipv6)
 561                do_main(PF_INET6);
 562
 563        return 0;
 564}
 565