timestamping.c 16 KB

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