linux/Documentation/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_SYS_HARDWARE - request reporting of transformed HW time stamps\n"
  80               "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
  81               "  SIOCGSTAMP - check last socket time stamp\n"
  82               "  SIOCGSTAMPNS - more accurate socket time stamp\n");
  83        exit(1);
  84}
  85
  86static void bail(const char *error)
  87{
  88        printf("%s: %s\n", error, strerror(errno));
  89        exit(1);
  90}
  91
  92static const unsigned char sync[] = {
  93        0x00, 0x01, 0x00, 0x01,
  94        0x5f, 0x44, 0x46, 0x4c,
  95        0x54, 0x00, 0x00, 0x00,
  96        0x00, 0x00, 0x00, 0x00,
  97        0x00, 0x00, 0x00, 0x00,
  98        0x01, 0x01,
  99
 100        /* fake uuid */
 101        0x00, 0x01,
 102        0x02, 0x03, 0x04, 0x05,
 103
 104        0x00, 0x01, 0x00, 0x37,
 105        0x00, 0x00, 0x00, 0x08,
 106        0x00, 0x00, 0x00, 0x00,
 107        0x49, 0x05, 0xcd, 0x01,
 108        0x29, 0xb1, 0x8d, 0xb0,
 109        0x00, 0x00, 0x00, 0x00,
 110        0x00, 0x01,
 111
 112        /* fake uuid */
 113        0x00, 0x01,
 114        0x02, 0x03, 0x04, 0x05,
 115
 116        0x00, 0x00, 0x00, 0x37,
 117        0x00, 0x00, 0x00, 0x04,
 118        0x44, 0x46, 0x4c, 0x54,
 119        0x00, 0x00, 0xf0, 0x60,
 120        0x00, 0x01, 0x00, 0x00,
 121        0x00, 0x00, 0x00, 0x01,
 122        0x00, 0x00, 0xf0, 0x60,
 123        0x00, 0x00, 0x00, 0x00,
 124        0x00, 0x00, 0x00, 0x04,
 125        0x44, 0x46, 0x4c, 0x54,
 126        0x00, 0x01,
 127
 128        /* fake uuid */
 129        0x00, 0x01,
 130        0x02, 0x03, 0x04, 0x05,
 131
 132        0x00, 0x00, 0x00, 0x00,
 133        0x00, 0x00, 0x00, 0x00,
 134        0x00, 0x00, 0x00, 0x00,
 135        0x00, 0x00, 0x00, 0x00
 136};
 137
 138static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
 139{
 140        struct timeval now;
 141        int res;
 142
 143        res = sendto(sock, sync, sizeof(sync), 0,
 144                addr, addr_len);
 145        gettimeofday(&now, 0);
 146        if (res < 0)
 147                printf("%s: %s\n", "send", strerror(errno));
 148        else
 149                printf("%ld.%06ld: sent %d bytes\n",
 150                       (long)now.tv_sec, (long)now.tv_usec,
 151                       res);
 152}
 153
 154static void printpacket(struct msghdr *msg, int res,
 155                        char *data,
 156                        int sock, int recvmsg_flags,
 157                        int siocgstamp, int siocgstampns)
 158{
 159        struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
 160        struct cmsghdr *cmsg;
 161        struct timeval tv;
 162        struct timespec ts;
 163        struct timeval now;
 164
 165        gettimeofday(&now, 0);
 166
 167        printf("%ld.%06ld: received %s data, %d bytes from %s, %d bytes control messages\n",
 168               (long)now.tv_sec, (long)now.tv_usec,
 169               (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
 170               res,
 171               inet_ntoa(from_addr->sin_addr),
 172               msg->msg_controllen);
 173        for (cmsg = CMSG_FIRSTHDR(msg);
 174             cmsg;
 175             cmsg = CMSG_NXTHDR(msg, cmsg)) {
 176                printf("   cmsg len %d: ", cmsg->cmsg_len);
 177                switch (cmsg->cmsg_level) {
 178                case SOL_SOCKET:
 179                        printf("SOL_SOCKET ");
 180                        switch (cmsg->cmsg_type) {
 181                        case SO_TIMESTAMP: {
 182                                struct timeval *stamp =
 183                                        (struct timeval *)CMSG_DATA(cmsg);
 184                                printf("SO_TIMESTAMP %ld.%06ld",
 185                                       (long)stamp->tv_sec,
 186                                       (long)stamp->tv_usec);
 187                                break;
 188                        }
 189                        case SO_TIMESTAMPNS: {
 190                                struct timespec *stamp =
 191                                        (struct timespec *)CMSG_DATA(cmsg);
 192                                printf("SO_TIMESTAMPNS %ld.%09ld",
 193                                       (long)stamp->tv_sec,
 194                                       (long)stamp->tv_nsec);
 195                                break;
 196                        }
 197                        case SO_TIMESTAMPING: {
 198                                struct timespec *stamp =
 199                                        (struct timespec *)CMSG_DATA(cmsg);
 200                                printf("SO_TIMESTAMPING ");
 201                                printf("SW %ld.%09ld ",
 202                                       (long)stamp->tv_sec,
 203                                       (long)stamp->tv_nsec);
 204                                stamp++;
 205                                printf("HW transformed %ld.%09ld ",
 206                                       (long)stamp->tv_sec,
 207                                       (long)stamp->tv_nsec);
 208                                stamp++;
 209                                printf("HW raw %ld.%09ld",
 210                                       (long)stamp->tv_sec,
 211                                       (long)stamp->tv_nsec);
 212                                break;
 213                        }
 214                        default:
 215                                printf("type %d", cmsg->cmsg_type);
 216                                break;
 217                        }
 218                        break;
 219                case IPPROTO_IP:
 220                        printf("IPPROTO_IP ");
 221                        switch (cmsg->cmsg_type) {
 222                        case IP_RECVERR: {
 223                                struct sock_extended_err *err =
 224                                        (struct sock_extended_err *)CMSG_DATA(cmsg);
 225                                printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
 226                                        strerror(err->ee_errno),
 227                                        err->ee_origin,
 228#ifdef SO_EE_ORIGIN_TIMESTAMPING
 229                                        err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
 230                                        "bounced packet" : "unexpected origin"
 231#else
 232                                        "probably SO_EE_ORIGIN_TIMESTAMPING"
 233#endif
 234                                        );
 235                                if (res < sizeof(sync))
 236                                        printf(" => truncated data?!");
 237                                else if (!memcmp(sync, data + res - sizeof(sync),
 238                                                        sizeof(sync)))
 239                                        printf(" => GOT OUR DATA BACK (HURRAY!)");
 240                                break;
 241                        }
 242                        case IP_PKTINFO: {
 243                                struct in_pktinfo *pktinfo =
 244                                        (struct in_pktinfo *)CMSG_DATA(cmsg);
 245                                printf("IP_PKTINFO interface index %u",
 246                                        pktinfo->ipi_ifindex);
 247                                break;
 248                        }
 249                        default:
 250                                printf("type %d", cmsg->cmsg_type);
 251                                break;
 252                        }
 253                        break;
 254                default:
 255                        printf("level %d type %d",
 256                                cmsg->cmsg_level,
 257                                cmsg->cmsg_type);
 258                        break;
 259                }
 260                printf("\n");
 261        }
 262
 263        if (siocgstamp) {
 264                if (ioctl(sock, SIOCGSTAMP, &tv))
 265                        printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
 266                else
 267                        printf("SIOCGSTAMP %ld.%06ld\n",
 268                               (long)tv.tv_sec,
 269                               (long)tv.tv_usec);
 270        }
 271        if (siocgstampns) {
 272                if (ioctl(sock, SIOCGSTAMPNS, &ts))
 273                        printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
 274                else
 275                        printf("SIOCGSTAMPNS %ld.%09ld\n",
 276                               (long)ts.tv_sec,
 277                               (long)ts.tv_nsec);
 278        }
 279}
 280
 281static void recvpacket(int sock, int recvmsg_flags,
 282                       int siocgstamp, int siocgstampns)
 283{
 284        char data[256];
 285        struct msghdr msg;
 286        struct iovec entry;
 287        struct sockaddr_in from_addr;
 288        struct {
 289                struct cmsghdr cm;
 290                char control[512];
 291        } control;
 292        int res;
 293
 294        memset(&msg, 0, sizeof(msg));
 295        msg.msg_iov = &entry;
 296        msg.msg_iovlen = 1;
 297        entry.iov_base = data;
 298        entry.iov_len = sizeof(data);
 299        msg.msg_name = (caddr_t)&from_addr;
 300        msg.msg_namelen = sizeof(from_addr);
 301        msg.msg_control = &control;
 302        msg.msg_controllen = sizeof(control);
 303
 304        res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
 305        if (res < 0) {
 306                printf("%s %s: %s\n",
 307                       "recvmsg",
 308                       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
 309                       strerror(errno));
 310        } else {
 311                printpacket(&msg, res, data,
 312                            sock, recvmsg_flags,
 313                            siocgstamp, siocgstampns);
 314        }
 315}
 316
 317int main(int argc, char **argv)
 318{
 319        int so_timestamping_flags = 0;
 320        int so_timestamp = 0;
 321        int so_timestampns = 0;
 322        int siocgstamp = 0;
 323        int siocgstampns = 0;
 324        int ip_multicast_loop = 0;
 325        char *interface;
 326        int i;
 327        int enabled = 1;
 328        int sock;
 329        struct ifreq device;
 330        struct ifreq hwtstamp;
 331        struct hwtstamp_config hwconfig, hwconfig_requested;
 332        struct sockaddr_in addr;
 333        struct ip_mreq imr;
 334        struct in_addr iaddr;
 335        int val;
 336        socklen_t len;
 337        struct timeval next;
 338
 339        if (argc < 2)
 340                usage(0);
 341        interface = argv[1];
 342
 343        for (i = 2; i < argc; i++) {
 344                if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
 345                        so_timestamp = 1;
 346                else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
 347                        so_timestampns = 1;
 348                else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
 349                        siocgstamp = 1;
 350                else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
 351                        siocgstampns = 1;
 352                else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
 353                        ip_multicast_loop = 1;
 354                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
 355                        so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
 356                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
 357                        so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
 358                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
 359                        so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
 360                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
 361                        so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
 362                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
 363                        so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
 364                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SYS_HARDWARE"))
 365                        so_timestamping_flags |= SOF_TIMESTAMPING_SYS_HARDWARE;
 366                else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
 367                        so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
 368                else
 369                        usage(argv[i]);
 370        }
 371
 372        sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 373        if (socket < 0)
 374                bail("socket");
 375
 376        memset(&device, 0, sizeof(device));
 377        strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
 378        if (ioctl(sock, SIOCGIFADDR, &device) < 0)
 379                bail("getting interface IP address");
 380
 381        memset(&hwtstamp, 0, sizeof(hwtstamp));
 382        strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
 383        hwtstamp.ifr_data = (void *)&hwconfig;
 384        memset(&hwconfig, 0, sizeof(hwconfig));
 385        hwconfig.tx_type =
 386                (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
 387                HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
 388        hwconfig.rx_filter =
 389                (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
 390                HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
 391        hwconfig_requested = hwconfig;
 392        if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
 393                if ((errno == EINVAL || errno == ENOTSUP) &&
 394                    hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
 395                    hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
 396                        printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
 397                else
 398                        bail("SIOCSHWTSTAMP");
 399        }
 400        printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
 401               hwconfig_requested.tx_type, hwconfig.tx_type,
 402               hwconfig_requested.rx_filter, hwconfig.rx_filter);
 403
 404        /* bind to PTP port */
 405        addr.sin_family = AF_INET;
 406        addr.sin_addr.s_addr = htonl(INADDR_ANY);
 407        addr.sin_port = htons(319 /* PTP event port */);
 408        if (bind(sock,
 409                 (struct sockaddr *)&addr,
 410                 sizeof(struct sockaddr_in)) < 0)
 411                bail("bind");
 412
 413        /* set multicast group for outgoing packets */
 414        inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
 415        addr.sin_addr = iaddr;
 416        imr.imr_multiaddr.s_addr = iaddr.s_addr;
 417        imr.imr_interface.s_addr =
 418                ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
 419        if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
 420                       &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
 421                bail("set multicast");
 422
 423        /* join multicast group, loop our own packet */
 424        if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
 425                       &imr, sizeof(struct ip_mreq)) < 0)
 426                bail("join multicast group");
 427
 428        if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
 429                       &ip_multicast_loop, sizeof(enabled)) < 0) {
 430                bail("loop multicast");
 431        }
 432
 433        /* set socket options for time stamping */
 434        if (so_timestamp &&
 435                setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
 436                           &enabled, sizeof(enabled)) < 0)
 437                bail("setsockopt SO_TIMESTAMP");
 438
 439        if (so_timestampns &&
 440                setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
 441                           &enabled, sizeof(enabled)) < 0)
 442                bail("setsockopt SO_TIMESTAMPNS");
 443
 444        if (so_timestamping_flags &&
 445                setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
 446                           &so_timestamping_flags,
 447                           sizeof(so_timestamping_flags)) < 0)
 448                bail("setsockopt SO_TIMESTAMPING");
 449
 450        /* request IP_PKTINFO for debugging purposes */
 451        if (setsockopt(sock, SOL_IP, IP_PKTINFO,
 452                       &enabled, sizeof(enabled)) < 0)
 453                printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
 454
 455        /* verify socket options */
 456        len = sizeof(val);
 457        if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
 458                printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
 459        else
 460                printf("SO_TIMESTAMP %d\n", val);
 461
 462        if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
 463                printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
 464                       strerror(errno));
 465        else
 466                printf("SO_TIMESTAMPNS %d\n", val);
 467
 468        if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
 469                printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
 470                       strerror(errno));
 471        } else {
 472                printf("SO_TIMESTAMPING %d\n", val);
 473                if (val != so_timestamping_flags)
 474                        printf("   not the expected value %d\n",
 475                               so_timestamping_flags);
 476        }
 477
 478        /* send packets forever every five seconds */
 479        gettimeofday(&next, 0);
 480        next.tv_sec = (next.tv_sec + 1) / 5 * 5;
 481        next.tv_usec = 0;
 482        while (1) {
 483                struct timeval now;
 484                struct timeval delta;
 485                long delta_us;
 486                int res;
 487                fd_set readfs, errorfs;
 488
 489                gettimeofday(&now, 0);
 490                delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
 491                        (long)(next.tv_usec - now.tv_usec);
 492                if (delta_us > 0) {
 493                        /* continue waiting for timeout or data */
 494                        delta.tv_sec = delta_us / 1000000;
 495                        delta.tv_usec = delta_us % 1000000;
 496
 497                        FD_ZERO(&readfs);
 498                        FD_ZERO(&errorfs);
 499                        FD_SET(sock, &readfs);
 500                        FD_SET(sock, &errorfs);
 501                        printf("%ld.%06ld: select %ldus\n",
 502                               (long)now.tv_sec, (long)now.tv_usec,
 503                               delta_us);
 504                        res = select(sock + 1, &readfs, 0, &errorfs, &delta);
 505                        gettimeofday(&now, 0);
 506                        printf("%ld.%06ld: select returned: %d, %s\n",
 507                               (long)now.tv_sec, (long)now.tv_usec,
 508                               res,
 509                               res < 0 ? strerror(errno) : "success");
 510                        if (res > 0) {
 511                                if (FD_ISSET(sock, &readfs))
 512                                        printf("ready for reading\n");
 513                                if (FD_ISSET(sock, &errorfs))
 514                                        printf("has error\n");
 515                                recvpacket(sock, 0,
 516                                           siocgstamp,
 517                                           siocgstampns);
 518                                recvpacket(sock, MSG_ERRQUEUE,
 519                                           siocgstamp,
 520                                           siocgstampns);
 521                        }
 522                } else {
 523                        /* write one packet */
 524                        sendpacket(sock,
 525                                   (struct sockaddr *)&addr,
 526                                   sizeof(addr));
 527                        next.tv_sec += 5;
 528                        continue;
 529                }
 530        }
 531
 532        return 0;
 533}
 534