linux/tools/testing/selftests/networking/timestamping/timestamping.c
<<
>>
Prefs
   1/*
   2 * This program demonstrates how the various time stamping features in
   3 * the Linux kernel work. It emulates the behavior of a PTP
   4 * implementation in stand-alone master mode by sending PTPv1 Sync
   5 * multicasts once every second. It looks for similar packets, but
   6 * beyond that doesn't actually implement PTP.
   7 *
   8 * Outgoing packets are time stamped with SO_TIMESTAMPING with or
   9 * without hardware support.
  10 *
  11 * Incoming packets are time stamped with SO_TIMESTAMPING with or
  12 * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
  13 * SO_TIMESTAMP[NS].
  14 *
  15 * Copyright (C) 2009 Intel Corporation.
  16 * Author: Patrick Ohly <patrick.ohly@intel.com>
  17 *
  18 * This program is free software; you can redistribute it and/or modify it
  19 * under the terms and conditions of the GNU General Public License,
  20 * version 2, as published by the Free Software Foundation.
  21 *
  22 * This program is distributed in the hope it will be useful, but WITHOUT
  23 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  24 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
  25 * more details.
  26 *
  27 * You should have received a copy of the GNU General Public License along with
  28 * this program; if not, write to the Free Software Foundation, Inc.,
  29 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  30 */
  31
  32#include <stdio.h>
  33#include <stdlib.h>
  34#include <errno.h>
  35#include <string.h>
  36
  37#include <sys/time.h>
  38#include <sys/socket.h>
  39#include <sys/select.h>
  40#include <sys/ioctl.h>
  41#include <arpa/inet.h>
  42#include <net/if.h>
  43
  44#include <asm/types.h>
  45#include <linux/net_tstamp.h>
  46#include <linux/errqueue.h>
  47
  48#ifndef SO_TIMESTAMPING
  49# define SO_TIMESTAMPING         37
  50# define SCM_TIMESTAMPING        SO_TIMESTAMPING
  51#endif
  52
  53#ifndef SO_TIMESTAMPNS
  54# define SO_TIMESTAMPNS 35
  55#endif
  56
  57#ifndef SIOCGSTAMPNS
  58# define SIOCGSTAMPNS 0x8907
  59#endif
  60
  61#ifndef SIOCSHWTSTAMP
  62# define SIOCSHWTSTAMP 0x89b0
  63#endif
  64
  65static void usage(const char *error)
  66{
  67        if (error)
  68                printf("invalid option: %s\n", error);
  69        printf("timestamping interface option*\n\n"
  70               "Options:\n"
  71               "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
  72               "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
  73               "  SO_TIMESTAMPNS - more accurate software time stamping\n"
  74               "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
  75               "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
  76               "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
  77               "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
  78               "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
  79               "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
  80               "  SIOCGSTAMP - check last socket time stamp\n"
  81               "  SIOCGSTAMPNS - more accurate socket time stamp\n");
  82        exit(1);
  83}
  84
  85static void bail(const char *error)
  86{
  87        printf("%s: %s\n", error, strerror(errno));
  88        exit(1);
  89}
  90
  91static const unsigned char sync[] = {
  92        0x00, 0x01, 0x00, 0x01,
  93        0x5f, 0x44, 0x46, 0x4c,
  94        0x54, 0x00, 0x00, 0x00,
  95        0x00, 0x00, 0x00, 0x00,
  96        0x00, 0x00, 0x00, 0x00,
  97        0x01, 0x01,
  98
  99        /* fake uuid */
 100        0x00, 0x01,
 101        0x02, 0x03, 0x04, 0x05,
 102
 103        0x00, 0x01, 0x00, 0x37,
 104        0x00, 0x00, 0x00, 0x08,
 105        0x00, 0x00, 0x00, 0x00,
 106        0x49, 0x05, 0xcd, 0x01,
 107        0x29, 0xb1, 0x8d, 0xb0,
 108        0x00, 0x00, 0x00, 0x00,
 109        0x00, 0x01,
 110
 111        /* fake uuid */
 112        0x00, 0x01,
 113        0x02, 0x03, 0x04, 0x05,
 114
 115        0x00, 0x00, 0x00, 0x37,
 116        0x00, 0x00, 0x00, 0x04,
 117        0x44, 0x46, 0x4c, 0x54,
 118        0x00, 0x00, 0xf0, 0x60,
 119        0x00, 0x01, 0x00, 0x00,
 120        0x00, 0x00, 0x00, 0x01,
 121        0x00, 0x00, 0xf0, 0x60,
 122        0x00, 0x00, 0x00, 0x00,
 123        0x00, 0x00, 0x00, 0x04,
 124        0x44, 0x46, 0x4c, 0x54,
 125        0x00, 0x01,
 126
 127        /* fake uuid */
 128        0x00, 0x01,
 129        0x02, 0x03, 0x04, 0x05,
 130
 131        0x00, 0x00, 0x00, 0x00,
 132        0x00, 0x00, 0x00, 0x00,
 133        0x00, 0x00, 0x00, 0x00,
 134        0x00, 0x00, 0x00, 0x00
 135};
 136
 137static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
 138{
 139        struct timeval now;
 140        int res;
 141
 142        res = sendto(sock, sync, sizeof(sync), 0,
 143                addr, addr_len);
 144        gettimeofday(&now, 0);
 145        if (res < 0)
 146                printf("%s: %s\n", "send", strerror(errno));
 147        else
 148                printf("%ld.%06ld: sent %d bytes\n",
 149                       (long)now.tv_sec, (long)now.tv_usec,
 150                       res);
 151}
 152
 153static void printpacket(struct msghdr *msg, int res,
 154                        char *data,
 155                        int sock, int recvmsg_flags,
 156                        int siocgstamp, int siocgstampns)
 157{
 158        struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
 159        struct cmsghdr *cmsg;
 160        struct timeval tv;
 161        struct timespec ts;
 162        struct timeval now;
 163
 164        gettimeofday(&now, 0);
 165
 166        printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
 167               (long)now.tv_sec, (long)now.tv_usec,
 168               (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
 169               res,
 170               inet_ntoa(from_addr->sin_addr),
 171               msg->msg_controllen);
 172        for (cmsg = CMSG_FIRSTHDR(msg);
 173             cmsg;
 174             cmsg = CMSG_NXTHDR(msg, cmsg)) {
 175                printf("   cmsg len %zu: ", cmsg->cmsg_len);
 176                switch (cmsg->cmsg_level) {
 177                case SOL_SOCKET:
 178                        printf("SOL_SOCKET ");
 179                        switch (cmsg->cmsg_type) {
 180                        case SO_TIMESTAMP: {
 181                                struct timeval *stamp =
 182                                        (struct timeval *)CMSG_DATA(cmsg);
 183                                printf("SO_TIMESTAMP %ld.%06ld",
 184                                       (long)stamp->tv_sec,
 185                                       (long)stamp->tv_usec);
 186                                break;
 187                        }
 188                        case SO_TIMESTAMPNS: {
 189                                struct timespec *stamp =
 190                                        (struct timespec *)CMSG_DATA(cmsg);
 191                                printf("SO_TIMESTAMPNS %ld.%09ld",
 192                                       (long)stamp->tv_sec,
 193                                       (long)stamp->tv_nsec);
 194                                break;
 195                        }
 196                        case SO_TIMESTAMPING: {
 197                                struct timespec *stamp =
 198                                        (struct timespec *)CMSG_DATA(cmsg);
 199                                printf("SO_TIMESTAMPING ");
 200                                printf("SW %ld.%09ld ",
 201                                       (long)stamp->tv_sec,
 202                                       (long)stamp->tv_nsec);
 203                                stamp++;
 204                                /* skip deprecated HW transformed */
 205                                stamp++;
 206                                printf("HW raw %ld.%09ld",
 207                                       (long)stamp->tv_sec,
 208                                       (long)stamp->tv_nsec);
 209                                break;
 210                        }
 211                        default:
 212                                printf("type %d", cmsg->cmsg_type);
 213                                break;
 214                        }
 215                        break;
 216                case IPPROTO_IP:
 217                        printf("IPPROTO_IP ");
 218                        switch (cmsg->cmsg_type) {
 219                        case IP_RECVERR: {
 220                                struct sock_extended_err *err =
 221                                        (struct sock_extended_err *)CMSG_DATA(cmsg);
 222                                printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
 223                                        strerror(err->ee_errno),
 224                                        err->ee_origin,
 225#ifdef SO_EE_ORIGIN_TIMESTAMPING
 226                                        err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
 227                                        "bounced packet" : "unexpected origin"
 228#else
 229                                        "probably SO_EE_ORIGIN_TIMESTAMPING"
 230#endif
 231                                        );
 232                                if (res < sizeof(sync))
 233                                        printf(" => truncated data?!");
 234                                else if (!memcmp(sync, data + res - sizeof(sync),
 235                                                        sizeof(sync)))
 236                                        printf(" => GOT OUR DATA BACK (HURRAY!)");
 237                                break;
 238                        }
 239                        case IP_PKTINFO: {
 240                                struct in_pktinfo *pktinfo =
 241                                        (struct in_pktinfo *)CMSG_DATA(cmsg);
 242                                printf("IP_PKTINFO interface index %u",
 243                                        pktinfo->ipi_ifindex);
 244                                break;
 245                        }
 246                        default:
 247                                printf("type %d", cmsg->cmsg_type);
 248                                break;
 249                        }
 250                        break;
 251                default:
 252                        printf("level %d type %d",
 253                                cmsg->cmsg_level,
 254                                cmsg->cmsg_type);
 255                        break;
 256                }
 257                printf("\n");
 258        }
 259
 260        if (siocgstamp) {
 261                if (ioctl(sock, SIOCGSTAMP, &tv))
 262                        printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
 263                else
 264                        printf("SIOCGSTAMP %ld.%06ld\n",
 265                               (long)tv.tv_sec,
 266                               (long)tv.tv_usec);
 267        }
 268        if (siocgstampns) {
 269                if (ioctl(sock, SIOCGSTAMPNS, &ts))
 270                        printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
 271                else
 272                        printf("SIOCGSTAMPNS %ld.%09ld\n",
 273                               (long)ts.tv_sec,
 274                               (long)ts.tv_nsec);
 275        }
 276}
 277
 278static void recvpacket(int sock, int recvmsg_flags,
 279                       int siocgstamp, int siocgstampns)
 280{
 281        char data[256];
 282        struct msghdr msg;
 283        struct iovec entry;
 284        struct sockaddr_in from_addr;
 285        struct {
 286                struct cmsghdr cm;
 287                char control[512];
 288        } control;
 289        int res;
 290
 291        memset(&msg, 0, sizeof(msg));
 292        msg.msg_iov = &entry;
 293        msg.msg_iovlen = 1;
 294        entry.iov_base = data;
 295        entry.iov_len = sizeof(data);
 296        msg.msg_name = (caddr_t)&from_addr;
 297        msg.msg_namelen = sizeof(from_addr);
 298        msg.msg_control = &control;
 299        msg.msg_controllen = sizeof(control);
 300
 301        res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
 302        if (res < 0) {
 303                printf("%s %s: %s\n",
 304                       "recvmsg",
 305                       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
 306                       strerror(errno));
 307        } else {
 308                printpacket(&msg, res, data,
 309                            sock, recvmsg_flags,
 310                            siocgstamp, siocgstampns);
 311        }
 312}
 313
 314int main(int argc, char **argv)
 315{
 316        int so_timestamping_flags = 0;
 317        int so_timestamp = 0;
 318        int so_timestampns = 0;
 319        int siocgstamp = 0;
 320        int siocgstampns = 0;
 321        int ip_multicast_loop = 0;
 322        char *interface;
 323        int i;
 324        int enabled = 1;
 325        int sock;
 326        struct ifreq device;
 327        struct ifreq hwtstamp;
 328        struct hwtstamp_config hwconfig, hwconfig_requested;
 329        struct sockaddr_in addr;
 330        struct ip_mreq imr;
 331        struct in_addr iaddr;
 332        int val;
 333        socklen_t len;
 334        struct timeval next;
 335
 336        if (argc < 2)
 337                usage(0);
 338        interface = argv[1];
 339
 340        for (i = 2; i < argc; i++) {
 341                if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
 342                        so_timestamp = 1;
 343                else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
 344                        so_timestampns = 1;
 345                else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
 346                        siocgstamp = 1;
 347                else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
 348                        siocgstampns = 1;
 349                else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
 350                        ip_multicast_loop = 1;
 351                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
 352                        so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
 353                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
 354                        so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
 355                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
 356                        so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
 357                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
 358                        so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
 359                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
 360                        so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
 361                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
 362                        so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
 363                else
 364                        usage(argv[i]);
 365        }
 366
 367        sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 368        if (sock < 0)
 369                bail("socket");
 370
 371        memset(&device, 0, sizeof(device));
 372        strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
 373        if (ioctl(sock, SIOCGIFADDR, &device) < 0)
 374                bail("getting interface IP address");
 375
 376        memset(&hwtstamp, 0, sizeof(hwtstamp));
 377        strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
 378        hwtstamp.ifr_data = (void *)&hwconfig;
 379        memset(&hwconfig, 0, sizeof(hwconfig));
 380        hwconfig.tx_type =
 381                (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
 382                HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
 383        hwconfig.rx_filter =
 384                (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
 385                HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
 386        hwconfig_requested = hwconfig;
 387        if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
 388                if ((errno == EINVAL || errno == ENOTSUP) &&
 389                    hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
 390                    hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
 391                        printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
 392                else
 393                        bail("SIOCSHWTSTAMP");
 394        }
 395        printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
 396               hwconfig_requested.tx_type, hwconfig.tx_type,
 397               hwconfig_requested.rx_filter, hwconfig.rx_filter);
 398
 399        /* bind to PTP port */
 400        addr.sin_family = AF_INET;
 401        addr.sin_addr.s_addr = htonl(INADDR_ANY);
 402        addr.sin_port = htons(319 /* PTP event port */);
 403        if (bind(sock,
 404                 (struct sockaddr *)&addr,
 405                 sizeof(struct sockaddr_in)) < 0)
 406                bail("bind");
 407
 408        /* set multicast group for outgoing packets */
 409        inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
 410        addr.sin_addr = iaddr;
 411        imr.imr_multiaddr.s_addr = iaddr.s_addr;
 412        imr.imr_interface.s_addr =
 413                ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
 414        if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
 415                       &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
 416                bail("set multicast");
 417
 418        /* join multicast group, loop our own packet */
 419        if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
 420                       &imr, sizeof(struct ip_mreq)) < 0)
 421                bail("join multicast group");
 422
 423        if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
 424                       &ip_multicast_loop, sizeof(enabled)) < 0) {
 425                bail("loop multicast");
 426        }
 427
 428        /* set socket options for time stamping */
 429        if (so_timestamp &&
 430                setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
 431                           &enabled, sizeof(enabled)) < 0)
 432                bail("setsockopt SO_TIMESTAMP");
 433
 434        if (so_timestampns &&
 435                setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
 436                           &enabled, sizeof(enabled)) < 0)
 437                bail("setsockopt SO_TIMESTAMPNS");
 438
 439        if (so_timestamping_flags &&
 440                setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
 441                           &so_timestamping_flags,
 442                           sizeof(so_timestamping_flags)) < 0)
 443                bail("setsockopt SO_TIMESTAMPING");
 444
 445        /* request IP_PKTINFO for debugging purposes */
 446        if (setsockopt(sock, SOL_IP, IP_PKTINFO,
 447                       &enabled, sizeof(enabled)) < 0)
 448                printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
 449
 450        /* verify socket options */
 451        len = sizeof(val);
 452        if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
 453                printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
 454        else
 455                printf("SO_TIMESTAMP %d\n", val);
 456
 457        if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
 458                printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
 459                       strerror(errno));
 460        else
 461                printf("SO_TIMESTAMPNS %d\n", val);
 462
 463        if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
 464                printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
 465                       strerror(errno));
 466        } else {
 467                printf("SO_TIMESTAMPING %d\n", val);
 468                if (val != so_timestamping_flags)
 469                        printf("   not the expected value %d\n",
 470                               so_timestamping_flags);
 471        }
 472
 473        /* send packets forever every five seconds */
 474        gettimeofday(&next, 0);
 475        next.tv_sec = (next.tv_sec + 1) / 5 * 5;
 476        next.tv_usec = 0;
 477        while (1) {
 478                struct timeval now;
 479                struct timeval delta;
 480                long delta_us;
 481                int res;
 482                fd_set readfs, errorfs;
 483
 484                gettimeofday(&now, 0);
 485                delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
 486                        (long)(next.tv_usec - now.tv_usec);
 487                if (delta_us > 0) {
 488                        /* continue waiting for timeout or data */
 489                        delta.tv_sec = delta_us / 1000000;
 490                        delta.tv_usec = delta_us % 1000000;
 491
 492                        FD_ZERO(&readfs);
 493                        FD_ZERO(&errorfs);
 494                        FD_SET(sock, &readfs);
 495                        FD_SET(sock, &errorfs);
 496                        printf("%ld.%06ld: select %ldus\n",
 497                               (long)now.tv_sec, (long)now.tv_usec,
 498                               delta_us);
 499                        res = select(sock + 1, &readfs, 0, &errorfs, &delta);
 500                        gettimeofday(&now, 0);
 501                        printf("%ld.%06ld: select returned: %d, %s\n",
 502                               (long)now.tv_sec, (long)now.tv_usec,
 503                               res,
 504                               res < 0 ? strerror(errno) : "success");
 505                        if (res > 0) {
 506                                if (FD_ISSET(sock, &readfs))
 507                                        printf("ready for reading\n");
 508                                if (FD_ISSET(sock, &errorfs))
 509                                        printf("has error\n");
 510                                recvpacket(sock, 0,
 511                                           siocgstamp,
 512                                           siocgstampns);
 513                                recvpacket(sock, MSG_ERRQUEUE,
 514                                           siocgstamp,
 515                                           siocgstampns);
 516                        }
 517                } else {
 518                        /* write one packet */
 519                        sendpacket(sock,
 520                                   (struct sockaddr *)&addr,
 521                                   sizeof(addr));
 522                        next.tv_sec += 5;
 523                        continue;
 524                }
 525        }
 526
 527        return 0;
 528}
 529