Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * This program demonstrates how the various time stamping features in
0004  * the Linux kernel work. It emulates the behavior of a PTP
0005  * implementation in stand-alone master mode by sending PTPv1 Sync
0006  * multicasts once every second. It looks for similar packets, but
0007  * beyond that doesn't actually implement PTP.
0008  *
0009  * Outgoing packets are time stamped with SO_TIMESTAMPING with or
0010  * without hardware support.
0011  *
0012  * Incoming packets are time stamped with SO_TIMESTAMPING with or
0013  * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
0014  * SO_TIMESTAMP[NS].
0015  *
0016  * Copyright (C) 2009 Intel Corporation.
0017  * Author: Patrick Ohly <patrick.ohly@intel.com>
0018  */
0019 
0020 #include <stdio.h>
0021 #include <stdlib.h>
0022 #include <errno.h>
0023 #include <string.h>
0024 
0025 #include <sys/time.h>
0026 #include <sys/socket.h>
0027 #include <sys/select.h>
0028 #include <sys/ioctl.h>
0029 #include <arpa/inet.h>
0030 #include <net/if.h>
0031 
0032 #include <asm/types.h>
0033 #include <linux/net_tstamp.h>
0034 #include <linux/errqueue.h>
0035 #include <linux/sockios.h>
0036 
0037 #ifndef SO_TIMESTAMPING
0038 # define SO_TIMESTAMPING         37
0039 # define SCM_TIMESTAMPING        SO_TIMESTAMPING
0040 #endif
0041 
0042 #ifndef SO_TIMESTAMPNS
0043 # define SO_TIMESTAMPNS 35
0044 #endif
0045 
0046 static void usage(const char *error)
0047 {
0048     if (error)
0049         printf("invalid option: %s\n", error);
0050     printf("timestamping <interface> [bind_phc_index] [option]*\n\n"
0051            "Options:\n"
0052            "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
0053            "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
0054            "  SO_TIMESTAMPNS - more accurate software time stamping\n"
0055            "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
0056            "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
0057            "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
0058            "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
0059            "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
0060            "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
0061            "  SOF_TIMESTAMPING_BIND_PHC - request to bind a PHC of PTP vclock\n"
0062            "  SIOCGSTAMP - check last socket time stamp\n"
0063            "  SIOCGSTAMPNS - more accurate socket time stamp\n"
0064            "  PTPV2 - use PTPv2 messages\n");
0065     exit(1);
0066 }
0067 
0068 static void bail(const char *error)
0069 {
0070     printf("%s: %s\n", error, strerror(errno));
0071     exit(1);
0072 }
0073 
0074 static const unsigned char sync[] = {
0075     0x00, 0x01, 0x00, 0x01,
0076     0x5f, 0x44, 0x46, 0x4c,
0077     0x54, 0x00, 0x00, 0x00,
0078     0x00, 0x00, 0x00, 0x00,
0079     0x00, 0x00, 0x00, 0x00,
0080     0x01, 0x01,
0081 
0082     /* fake uuid */
0083     0x00, 0x01,
0084     0x02, 0x03, 0x04, 0x05,
0085 
0086     0x00, 0x01, 0x00, 0x37,
0087     0x00, 0x00, 0x00, 0x08,
0088     0x00, 0x00, 0x00, 0x00,
0089     0x49, 0x05, 0xcd, 0x01,
0090     0x29, 0xb1, 0x8d, 0xb0,
0091     0x00, 0x00, 0x00, 0x00,
0092     0x00, 0x01,
0093 
0094     /* fake uuid */
0095     0x00, 0x01,
0096     0x02, 0x03, 0x04, 0x05,
0097 
0098     0x00, 0x00, 0x00, 0x37,
0099     0x00, 0x00, 0x00, 0x04,
0100     0x44, 0x46, 0x4c, 0x54,
0101     0x00, 0x00, 0xf0, 0x60,
0102     0x00, 0x01, 0x00, 0x00,
0103     0x00, 0x00, 0x00, 0x01,
0104     0x00, 0x00, 0xf0, 0x60,
0105     0x00, 0x00, 0x00, 0x00,
0106     0x00, 0x00, 0x00, 0x04,
0107     0x44, 0x46, 0x4c, 0x54,
0108     0x00, 0x01,
0109 
0110     /* fake uuid */
0111     0x00, 0x01,
0112     0x02, 0x03, 0x04, 0x05,
0113 
0114     0x00, 0x00, 0x00, 0x00,
0115     0x00, 0x00, 0x00, 0x00,
0116     0x00, 0x00, 0x00, 0x00,
0117     0x00, 0x00, 0x00, 0x00
0118 };
0119 
0120 static const unsigned char sync_v2[] = {
0121     0x00, 0x02, 0x00, 0x2C,
0122     0x00, 0x00, 0x02, 0x00,
0123     0x00, 0x00, 0x00, 0x00,
0124     0x00, 0x00, 0x00, 0x00,
0125     0x00, 0x00, 0x00, 0x00,
0126     0x00, 0x00, 0x00, 0xFF,
0127     0xFE, 0x00, 0x00, 0x00,
0128     0x00, 0x01, 0x00, 0x01,
0129     0x00, 0x00, 0x00, 0x00,
0130     0x00, 0x00, 0x00, 0x00,
0131     0x00, 0x00, 0x00, 0x00,
0132 };
0133 
0134 static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len, int ptpv2)
0135 {
0136     size_t sync_len = ptpv2 ? sizeof(sync_v2) : sizeof(sync);
0137     const void *sync_p = ptpv2 ? sync_v2 : sync;
0138     struct timeval now;
0139     int res;
0140 
0141     res = sendto(sock, sync_p, sync_len, 0, addr, addr_len);
0142     gettimeofday(&now, 0);
0143     if (res < 0)
0144         printf("%s: %s\n", "send", strerror(errno));
0145     else
0146         printf("%ld.%06ld: sent %d bytes\n",
0147                (long)now.tv_sec, (long)now.tv_usec,
0148                res);
0149 }
0150 
0151 static void printpacket(struct msghdr *msg, int res,
0152             char *data,
0153             int sock, int recvmsg_flags,
0154             int siocgstamp, int siocgstampns, int ptpv2)
0155 {
0156     struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
0157     size_t sync_len = ptpv2 ? sizeof(sync_v2) : sizeof(sync);
0158     const void *sync_p = ptpv2 ? sync_v2 : sync;
0159     struct cmsghdr *cmsg;
0160     struct timeval tv;
0161     struct timespec ts;
0162     struct timeval now;
0163 
0164     gettimeofday(&now, 0);
0165 
0166     printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
0167            (long)now.tv_sec, (long)now.tv_usec,
0168            (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
0169            res,
0170            inet_ntoa(from_addr->sin_addr),
0171            msg->msg_controllen);
0172     for (cmsg = CMSG_FIRSTHDR(msg);
0173          cmsg;
0174          cmsg = CMSG_NXTHDR(msg, cmsg)) {
0175         printf("   cmsg len %zu: ", cmsg->cmsg_len);
0176         switch (cmsg->cmsg_level) {
0177         case SOL_SOCKET:
0178             printf("SOL_SOCKET ");
0179             switch (cmsg->cmsg_type) {
0180             case SO_TIMESTAMP: {
0181                 struct timeval *stamp =
0182                     (struct timeval *)CMSG_DATA(cmsg);
0183                 printf("SO_TIMESTAMP %ld.%06ld",
0184                        (long)stamp->tv_sec,
0185                        (long)stamp->tv_usec);
0186                 break;
0187             }
0188             case SO_TIMESTAMPNS: {
0189                 struct timespec *stamp =
0190                     (struct timespec *)CMSG_DATA(cmsg);
0191                 printf("SO_TIMESTAMPNS %ld.%09ld",
0192                        (long)stamp->tv_sec,
0193                        (long)stamp->tv_nsec);
0194                 break;
0195             }
0196             case SO_TIMESTAMPING: {
0197                 struct timespec *stamp =
0198                     (struct timespec *)CMSG_DATA(cmsg);
0199                 printf("SO_TIMESTAMPING ");
0200                 printf("SW %ld.%09ld ",
0201                        (long)stamp->tv_sec,
0202                        (long)stamp->tv_nsec);
0203                 stamp++;
0204                 /* skip deprecated HW transformed */
0205                 stamp++;
0206                 printf("HW raw %ld.%09ld",
0207                        (long)stamp->tv_sec,
0208                        (long)stamp->tv_nsec);
0209                 break;
0210             }
0211             default:
0212                 printf("type %d", cmsg->cmsg_type);
0213                 break;
0214             }
0215             break;
0216         case IPPROTO_IP:
0217             printf("IPPROTO_IP ");
0218             switch (cmsg->cmsg_type) {
0219             case IP_RECVERR: {
0220                 struct sock_extended_err *err =
0221                     (struct sock_extended_err *)CMSG_DATA(cmsg);
0222                 printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
0223                     strerror(err->ee_errno),
0224                     err->ee_origin,
0225 #ifdef SO_EE_ORIGIN_TIMESTAMPING
0226                     err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
0227                     "bounced packet" : "unexpected origin"
0228 #else
0229                     "probably SO_EE_ORIGIN_TIMESTAMPING"
0230 #endif
0231                     );
0232                 if (res < sync_len)
0233                     printf(" => truncated data?!");
0234                 else if (!memcmp(sync_p, data + res - sync_len, sync_len))
0235                     printf(" => GOT OUR DATA BACK (HURRAY!)");
0236                 break;
0237             }
0238             case IP_PKTINFO: {
0239                 struct in_pktinfo *pktinfo =
0240                     (struct in_pktinfo *)CMSG_DATA(cmsg);
0241                 printf("IP_PKTINFO interface index %u",
0242                     pktinfo->ipi_ifindex);
0243                 break;
0244             }
0245             default:
0246                 printf("type %d", cmsg->cmsg_type);
0247                 break;
0248             }
0249             break;
0250         default:
0251             printf("level %d type %d",
0252                 cmsg->cmsg_level,
0253                 cmsg->cmsg_type);
0254             break;
0255         }
0256         printf("\n");
0257     }
0258 
0259     if (siocgstamp) {
0260         if (ioctl(sock, SIOCGSTAMP, &tv))
0261             printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
0262         else
0263             printf("SIOCGSTAMP %ld.%06ld\n",
0264                    (long)tv.tv_sec,
0265                    (long)tv.tv_usec);
0266     }
0267     if (siocgstampns) {
0268         if (ioctl(sock, SIOCGSTAMPNS, &ts))
0269             printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
0270         else
0271             printf("SIOCGSTAMPNS %ld.%09ld\n",
0272                    (long)ts.tv_sec,
0273                    (long)ts.tv_nsec);
0274     }
0275 }
0276 
0277 static void recvpacket(int sock, int recvmsg_flags,
0278                int siocgstamp, int siocgstampns, int ptpv2)
0279 {
0280     char data[256];
0281     struct msghdr msg;
0282     struct iovec entry;
0283     struct sockaddr_in from_addr;
0284     struct {
0285         struct cmsghdr cm;
0286         char control[512];
0287     } control;
0288     int res;
0289 
0290     memset(&msg, 0, sizeof(msg));
0291     msg.msg_iov = &entry;
0292     msg.msg_iovlen = 1;
0293     entry.iov_base = data;
0294     entry.iov_len = sizeof(data);
0295     msg.msg_name = (caddr_t)&from_addr;
0296     msg.msg_namelen = sizeof(from_addr);
0297     msg.msg_control = &control;
0298     msg.msg_controllen = sizeof(control);
0299 
0300     res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
0301     if (res < 0) {
0302         printf("%s %s: %s\n",
0303                "recvmsg",
0304                (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
0305                strerror(errno));
0306     } else {
0307         printpacket(&msg, res, data,
0308                 sock, recvmsg_flags,
0309                 siocgstamp, siocgstampns, ptpv2);
0310     }
0311 }
0312 
0313 int main(int argc, char **argv)
0314 {
0315     int so_timestamp = 0;
0316     int so_timestampns = 0;
0317     int siocgstamp = 0;
0318     int siocgstampns = 0;
0319     int ip_multicast_loop = 0;
0320     int ptpv2 = 0;
0321     char *interface;
0322     int i;
0323     int enabled = 1;
0324     int sock;
0325     struct ifreq device;
0326     struct ifreq hwtstamp;
0327     struct hwtstamp_config hwconfig, hwconfig_requested;
0328     struct so_timestamping so_timestamping_get = { 0, 0 };
0329     struct so_timestamping so_timestamping = { 0, 0 };
0330     struct sockaddr_in addr;
0331     struct ip_mreq imr;
0332     struct in_addr iaddr;
0333     int val;
0334     socklen_t len;
0335     struct timeval next;
0336     size_t if_len;
0337 
0338     if (argc < 2)
0339         usage(0);
0340     interface = argv[1];
0341     if_len = strlen(interface);
0342     if (if_len >= IFNAMSIZ) {
0343         printf("interface name exceeds IFNAMSIZ\n");
0344         exit(1);
0345     }
0346 
0347     if (argc >= 3 && sscanf(argv[2], "%d", &so_timestamping.bind_phc) == 1)
0348         val = 3;
0349     else
0350         val = 2;
0351 
0352     for (i = val; i < argc; i++) {
0353         if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
0354             so_timestamp = 1;
0355         else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
0356             so_timestampns = 1;
0357         else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
0358             siocgstamp = 1;
0359         else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
0360             siocgstampns = 1;
0361         else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
0362             ip_multicast_loop = 1;
0363         else if (!strcasecmp(argv[i], "PTPV2"))
0364             ptpv2 = 1;
0365         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
0366             so_timestamping.flags |= SOF_TIMESTAMPING_TX_HARDWARE;
0367         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
0368             so_timestamping.flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
0369         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
0370             so_timestamping.flags |= SOF_TIMESTAMPING_RX_HARDWARE;
0371         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
0372             so_timestamping.flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
0373         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
0374             so_timestamping.flags |= SOF_TIMESTAMPING_SOFTWARE;
0375         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
0376             so_timestamping.flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
0377         else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_BIND_PHC"))
0378             so_timestamping.flags |= SOF_TIMESTAMPING_BIND_PHC;
0379         else
0380             usage(argv[i]);
0381     }
0382 
0383     sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
0384     if (sock < 0)
0385         bail("socket");
0386 
0387     memset(&device, 0, sizeof(device));
0388     memcpy(device.ifr_name, interface, if_len + 1);
0389     if (ioctl(sock, SIOCGIFADDR, &device) < 0)
0390         bail("getting interface IP address");
0391 
0392     memset(&hwtstamp, 0, sizeof(hwtstamp));
0393     memcpy(hwtstamp.ifr_name, interface, if_len + 1);
0394     hwtstamp.ifr_data = (void *)&hwconfig;
0395     memset(&hwconfig, 0, sizeof(hwconfig));
0396     hwconfig.tx_type =
0397         (so_timestamping.flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
0398         HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
0399     hwconfig.rx_filter =
0400         (so_timestamping.flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
0401         ptpv2 ? HWTSTAMP_FILTER_PTP_V2_L4_SYNC :
0402         HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
0403     hwconfig_requested = hwconfig;
0404     if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
0405         if ((errno == EINVAL || errno == ENOTSUP) &&
0406             hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
0407             hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
0408             printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
0409         else
0410             bail("SIOCSHWTSTAMP");
0411     }
0412     printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
0413            hwconfig_requested.tx_type, hwconfig.tx_type,
0414            hwconfig_requested.rx_filter, hwconfig.rx_filter);
0415 
0416     /* bind to PTP port */
0417     addr.sin_family = AF_INET;
0418     addr.sin_addr.s_addr = htonl(INADDR_ANY);
0419     addr.sin_port = htons(319 /* PTP event port */);
0420     if (bind(sock,
0421          (struct sockaddr *)&addr,
0422          sizeof(struct sockaddr_in)) < 0)
0423         bail("bind");
0424 
0425     if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface, if_len))
0426         bail("bind device");
0427 
0428     /* set multicast group for outgoing packets */
0429     inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
0430     addr.sin_addr = iaddr;
0431     imr.imr_multiaddr.s_addr = iaddr.s_addr;
0432     imr.imr_interface.s_addr =
0433         ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
0434     if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
0435                &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
0436         bail("set multicast");
0437 
0438     /* join multicast group, loop our own packet */
0439     if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
0440                &imr, sizeof(struct ip_mreq)) < 0)
0441         bail("join multicast group");
0442 
0443     if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
0444                &ip_multicast_loop, sizeof(enabled)) < 0) {
0445         bail("loop multicast");
0446     }
0447 
0448     /* set socket options for time stamping */
0449     if (so_timestamp &&
0450         setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
0451                &enabled, sizeof(enabled)) < 0)
0452         bail("setsockopt SO_TIMESTAMP");
0453 
0454     if (so_timestampns &&
0455         setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
0456                &enabled, sizeof(enabled)) < 0)
0457         bail("setsockopt SO_TIMESTAMPNS");
0458 
0459     if (so_timestamping.flags &&
0460         setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping,
0461                sizeof(so_timestamping)) < 0)
0462         bail("setsockopt SO_TIMESTAMPING");
0463 
0464     /* request IP_PKTINFO for debugging purposes */
0465     if (setsockopt(sock, SOL_IP, IP_PKTINFO,
0466                &enabled, sizeof(enabled)) < 0)
0467         printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
0468 
0469     /* verify socket options */
0470     len = sizeof(val);
0471     if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
0472         printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
0473     else
0474         printf("SO_TIMESTAMP %d\n", val);
0475 
0476     if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
0477         printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
0478                strerror(errno));
0479     else
0480         printf("SO_TIMESTAMPNS %d\n", val);
0481 
0482     len = sizeof(so_timestamping_get);
0483     if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_get,
0484                &len) < 0) {
0485         printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
0486                strerror(errno));
0487     } else {
0488         printf("SO_TIMESTAMPING flags %d, bind phc %d\n",
0489                so_timestamping_get.flags, so_timestamping_get.bind_phc);
0490         if (so_timestamping_get.flags != so_timestamping.flags ||
0491             so_timestamping_get.bind_phc != so_timestamping.bind_phc)
0492             printf("   not expected, flags %d, bind phc %d\n",
0493                    so_timestamping.flags, so_timestamping.bind_phc);
0494     }
0495 
0496     /* send packets forever every five seconds */
0497     gettimeofday(&next, 0);
0498     next.tv_sec = (next.tv_sec + 1) / 5 * 5;
0499     next.tv_usec = 0;
0500     while (1) {
0501         struct timeval now;
0502         struct timeval delta;
0503         long delta_us;
0504         int res;
0505         fd_set readfs, errorfs;
0506 
0507         gettimeofday(&now, 0);
0508         delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
0509             (long)(next.tv_usec - now.tv_usec);
0510         if (delta_us > 0) {
0511             /* continue waiting for timeout or data */
0512             delta.tv_sec = delta_us / 1000000;
0513             delta.tv_usec = delta_us % 1000000;
0514 
0515             FD_ZERO(&readfs);
0516             FD_ZERO(&errorfs);
0517             FD_SET(sock, &readfs);
0518             FD_SET(sock, &errorfs);
0519             printf("%ld.%06ld: select %ldus\n",
0520                    (long)now.tv_sec, (long)now.tv_usec,
0521                    delta_us);
0522             res = select(sock + 1, &readfs, 0, &errorfs, &delta);
0523             gettimeofday(&now, 0);
0524             printf("%ld.%06ld: select returned: %d, %s\n",
0525                    (long)now.tv_sec, (long)now.tv_usec,
0526                    res,
0527                    res < 0 ? strerror(errno) : "success");
0528             if (res > 0) {
0529                 if (FD_ISSET(sock, &readfs))
0530                     printf("ready for reading\n");
0531                 if (FD_ISSET(sock, &errorfs))
0532                     printf("has error\n");
0533                 recvpacket(sock, 0,
0534                        siocgstamp,
0535                        siocgstampns, ptpv2);
0536                 recvpacket(sock, MSG_ERRQUEUE,
0537                        siocgstamp,
0538                        siocgstampns, ptpv2);
0539             }
0540         } else {
0541             /* write one packet */
0542             sendpacket(sock,
0543                    (struct sockaddr *)&addr,
0544                    sizeof(addr), ptpv2);
0545             next.tv_sec += 5;
0546             continue;
0547         }
0548     }
0549 
0550     return 0;
0551 }