timestamping.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <errno.h>
  34. #include <string.h>
  35. #include <sys/time.h>
  36. #include <sys/socket.h>
  37. #include <sys/select.h>
  38. #include <sys/ioctl.h>
  39. #include <arpa/inet.h>
  40. #include <net/if.h>
  41. #include <asm/types.h>
  42. #include <linux/net_tstamp.h>
  43. #include <linux/errqueue.h>
  44. #ifndef SO_TIMESTAMPING
  45. # define SO_TIMESTAMPING 37
  46. # define SCM_TIMESTAMPING SO_TIMESTAMPING
  47. #endif
  48. #ifndef SO_TIMESTAMPNS
  49. # define SO_TIMESTAMPNS 35
  50. #endif
  51. #ifndef SIOCGSTAMPNS
  52. # define SIOCGSTAMPNS 0x8907
  53. #endif
  54. #ifndef SIOCSHWTSTAMP
  55. # define SIOCSHWTSTAMP 0x89b0
  56. #endif
  57. static void usage(const char *error)
  58. {
  59. if (error)
  60. printf("invalid option: %s\n", error);
  61. printf("timestamping interface option*\n\n"
  62. "Options:\n"
  63. " IP_MULTICAST_LOOP - looping outgoing multicasts\n"
  64. " SO_TIMESTAMP - normal software time stamping, ms resolution\n"
  65. " SO_TIMESTAMPNS - more accurate software time stamping\n"
  66. " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
  67. " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
  68. " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
  69. " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
  70. " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
  71. " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
  72. " SIOCGSTAMP - check last socket time stamp\n"
  73. " SIOCGSTAMPNS - more accurate socket time stamp\n");
  74. exit(1);
  75. }
  76. static void bail(const char *error)
  77. {
  78. printf("%s: %s\n", error, strerror(errno));
  79. exit(1);
  80. }
  81. static const unsigned char sync[] = {
  82. 0x00, 0x01, 0x00, 0x01,
  83. 0x5f, 0x44, 0x46, 0x4c,
  84. 0x54, 0x00, 0x00, 0x00,
  85. 0x00, 0x00, 0x00, 0x00,
  86. 0x00, 0x00, 0x00, 0x00,
  87. 0x01, 0x01,
  88. /* fake uuid */
  89. 0x00, 0x01,
  90. 0x02, 0x03, 0x04, 0x05,
  91. 0x00, 0x01, 0x00, 0x37,
  92. 0x00, 0x00, 0x00, 0x08,
  93. 0x00, 0x00, 0x00, 0x00,
  94. 0x49, 0x05, 0xcd, 0x01,
  95. 0x29, 0xb1, 0x8d, 0xb0,
  96. 0x00, 0x00, 0x00, 0x00,
  97. 0x00, 0x01,
  98. /* fake uuid */
  99. 0x00, 0x01,
  100. 0x02, 0x03, 0x04, 0x05,
  101. 0x00, 0x00, 0x00, 0x37,
  102. 0x00, 0x00, 0x00, 0x04,
  103. 0x44, 0x46, 0x4c, 0x54,
  104. 0x00, 0x00, 0xf0, 0x60,
  105. 0x00, 0x01, 0x00, 0x00,
  106. 0x00, 0x00, 0x00, 0x01,
  107. 0x00, 0x00, 0xf0, 0x60,
  108. 0x00, 0x00, 0x00, 0x00,
  109. 0x00, 0x00, 0x00, 0x04,
  110. 0x44, 0x46, 0x4c, 0x54,
  111. 0x00, 0x01,
  112. /* fake uuid */
  113. 0x00, 0x01,
  114. 0x02, 0x03, 0x04, 0x05,
  115. 0x00, 0x00, 0x00, 0x00,
  116. 0x00, 0x00, 0x00, 0x00,
  117. 0x00, 0x00, 0x00, 0x00,
  118. 0x00, 0x00, 0x00, 0x00
  119. };
  120. static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
  121. {
  122. struct timeval now;
  123. int res;
  124. res = sendto(sock, sync, sizeof(sync), 0,
  125. addr, addr_len);
  126. gettimeofday(&now, 0);
  127. if (res < 0)
  128. printf("%s: %s\n", "send", strerror(errno));
  129. else
  130. printf("%ld.%06ld: sent %d bytes\n",
  131. (long)now.tv_sec, (long)now.tv_usec,
  132. res);
  133. }
  134. static void printpacket(struct msghdr *msg, int res,
  135. char *data,
  136. int sock, int recvmsg_flags,
  137. int siocgstamp, int siocgstampns)
  138. {
  139. struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
  140. struct cmsghdr *cmsg;
  141. struct timeval tv;
  142. struct timespec ts;
  143. struct timeval now;
  144. gettimeofday(&now, 0);
  145. printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
  146. (long)now.tv_sec, (long)now.tv_usec,
  147. (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
  148. res,
  149. inet_ntoa(from_addr->sin_addr),
  150. msg->msg_controllen);
  151. for (cmsg = CMSG_FIRSTHDR(msg);
  152. cmsg;
  153. cmsg = CMSG_NXTHDR(msg, cmsg)) {
  154. printf(" cmsg len %zu: ", cmsg->cmsg_len);
  155. switch (cmsg->cmsg_level) {
  156. case SOL_SOCKET:
  157. printf("SOL_SOCKET ");
  158. switch (cmsg->cmsg_type) {
  159. case SO_TIMESTAMP: {
  160. struct timeval *stamp =
  161. (struct timeval *)CMSG_DATA(cmsg);
  162. printf("SO_TIMESTAMP %ld.%06ld",
  163. (long)stamp->tv_sec,
  164. (long)stamp->tv_usec);
  165. break;
  166. }
  167. case SO_TIMESTAMPNS: {
  168. struct timespec *stamp =
  169. (struct timespec *)CMSG_DATA(cmsg);
  170. printf("SO_TIMESTAMPNS %ld.%09ld",
  171. (long)stamp->tv_sec,
  172. (long)stamp->tv_nsec);
  173. break;
  174. }
  175. case SO_TIMESTAMPING: {
  176. struct timespec *stamp =
  177. (struct timespec *)CMSG_DATA(cmsg);
  178. printf("SO_TIMESTAMPING ");
  179. printf("SW %ld.%09ld ",
  180. (long)stamp->tv_sec,
  181. (long)stamp->tv_nsec);
  182. stamp++;
  183. /* skip deprecated HW transformed */
  184. stamp++;
  185. printf("HW raw %ld.%09ld",
  186. (long)stamp->tv_sec,
  187. (long)stamp->tv_nsec);
  188. break;
  189. }
  190. default:
  191. printf("type %d", cmsg->cmsg_type);
  192. break;
  193. }
  194. break;
  195. case IPPROTO_IP:
  196. printf("IPPROTO_IP ");
  197. switch (cmsg->cmsg_type) {
  198. case IP_RECVERR: {
  199. struct sock_extended_err *err =
  200. (struct sock_extended_err *)CMSG_DATA(cmsg);
  201. printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
  202. strerror(err->ee_errno),
  203. err->ee_origin,
  204. #ifdef SO_EE_ORIGIN_TIMESTAMPING
  205. err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
  206. "bounced packet" : "unexpected origin"
  207. #else
  208. "probably SO_EE_ORIGIN_TIMESTAMPING"
  209. #endif
  210. );
  211. if (res < sizeof(sync))
  212. printf(" => truncated data?!");
  213. else if (!memcmp(sync, data + res - sizeof(sync),
  214. sizeof(sync)))
  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)
  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);
  286. }
  287. }
  288. int main(int argc, char **argv)
  289. {
  290. int so_timestamping_flags = 0;
  291. int so_timestamp = 0;
  292. int so_timestampns = 0;
  293. int siocgstamp = 0;
  294. int siocgstampns = 0;
  295. int ip_multicast_loop = 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 sockaddr_in addr;
  304. struct ip_mreq imr;
  305. struct in_addr iaddr;
  306. int val;
  307. socklen_t len;
  308. struct timeval next;
  309. size_t if_len;
  310. if (argc < 2)
  311. usage(0);
  312. interface = argv[1];
  313. if_len = strlen(interface);
  314. if (if_len >= IFNAMSIZ) {
  315. printf("interface name exceeds IFNAMSIZ\n");
  316. exit(1);
  317. }
  318. for (i = 2; i < argc; i++) {
  319. if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
  320. so_timestamp = 1;
  321. else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
  322. so_timestampns = 1;
  323. else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
  324. siocgstamp = 1;
  325. else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
  326. siocgstampns = 1;
  327. else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
  328. ip_multicast_loop = 1;
  329. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
  330. so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
  331. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
  332. so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
  333. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
  334. so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
  335. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
  336. so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
  337. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
  338. so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
  339. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
  340. so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
  341. else
  342. usage(argv[i]);
  343. }
  344. sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  345. if (sock < 0)
  346. bail("socket");
  347. memset(&device, 0, sizeof(device));
  348. memcpy(device.ifr_name, interface, if_len + 1);
  349. if (ioctl(sock, SIOCGIFADDR, &device) < 0)
  350. bail("getting interface IP address");
  351. memset(&hwtstamp, 0, sizeof(hwtstamp));
  352. memcpy(hwtstamp.ifr_name, interface, if_len + 1);
  353. hwtstamp.ifr_data = (void *)&hwconfig;
  354. memset(&hwconfig, 0, sizeof(hwconfig));
  355. hwconfig.tx_type =
  356. (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
  357. HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
  358. hwconfig.rx_filter =
  359. (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
  360. HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
  361. hwconfig_requested = hwconfig;
  362. if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
  363. if ((errno == EINVAL || errno == ENOTSUP) &&
  364. hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
  365. hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
  366. printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
  367. else
  368. bail("SIOCSHWTSTAMP");
  369. }
  370. printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
  371. hwconfig_requested.tx_type, hwconfig.tx_type,
  372. hwconfig_requested.rx_filter, hwconfig.rx_filter);
  373. /* bind to PTP port */
  374. addr.sin_family = AF_INET;
  375. addr.sin_addr.s_addr = htonl(INADDR_ANY);
  376. addr.sin_port = htons(319 /* PTP event port */);
  377. if (bind(sock,
  378. (struct sockaddr *)&addr,
  379. sizeof(struct sockaddr_in)) < 0)
  380. bail("bind");
  381. /* set multicast group for outgoing packets */
  382. inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
  383. addr.sin_addr = iaddr;
  384. imr.imr_multiaddr.s_addr = iaddr.s_addr;
  385. imr.imr_interface.s_addr =
  386. ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
  387. if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
  388. &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
  389. bail("set multicast");
  390. /* join multicast group, loop our own packet */
  391. if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
  392. &imr, sizeof(struct ip_mreq)) < 0)
  393. bail("join multicast group");
  394. if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
  395. &ip_multicast_loop, sizeof(enabled)) < 0) {
  396. bail("loop multicast");
  397. }
  398. /* set socket options for time stamping */
  399. if (so_timestamp &&
  400. setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
  401. &enabled, sizeof(enabled)) < 0)
  402. bail("setsockopt SO_TIMESTAMP");
  403. if (so_timestampns &&
  404. setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
  405. &enabled, sizeof(enabled)) < 0)
  406. bail("setsockopt SO_TIMESTAMPNS");
  407. if (so_timestamping_flags &&
  408. setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
  409. &so_timestamping_flags,
  410. sizeof(so_timestamping_flags)) < 0)
  411. bail("setsockopt SO_TIMESTAMPING");
  412. /* request IP_PKTINFO for debugging purposes */
  413. if (setsockopt(sock, SOL_IP, IP_PKTINFO,
  414. &enabled, sizeof(enabled)) < 0)
  415. printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
  416. /* verify socket options */
  417. len = sizeof(val);
  418. if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
  419. printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
  420. else
  421. printf("SO_TIMESTAMP %d\n", val);
  422. if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
  423. printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
  424. strerror(errno));
  425. else
  426. printf("SO_TIMESTAMPNS %d\n", val);
  427. if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
  428. printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
  429. strerror(errno));
  430. } else {
  431. printf("SO_TIMESTAMPING %d\n", val);
  432. if (val != so_timestamping_flags)
  433. printf(" not the expected value %d\n",
  434. so_timestamping_flags);
  435. }
  436. /* send packets forever every five seconds */
  437. gettimeofday(&next, 0);
  438. next.tv_sec = (next.tv_sec + 1) / 5 * 5;
  439. next.tv_usec = 0;
  440. while (1) {
  441. struct timeval now;
  442. struct timeval delta;
  443. long delta_us;
  444. int res;
  445. fd_set readfs, errorfs;
  446. gettimeofday(&now, 0);
  447. delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
  448. (long)(next.tv_usec - now.tv_usec);
  449. if (delta_us > 0) {
  450. /* continue waiting for timeout or data */
  451. delta.tv_sec = delta_us / 1000000;
  452. delta.tv_usec = delta_us % 1000000;
  453. FD_ZERO(&readfs);
  454. FD_ZERO(&errorfs);
  455. FD_SET(sock, &readfs);
  456. FD_SET(sock, &errorfs);
  457. printf("%ld.%06ld: select %ldus\n",
  458. (long)now.tv_sec, (long)now.tv_usec,
  459. delta_us);
  460. res = select(sock + 1, &readfs, 0, &errorfs, &delta);
  461. gettimeofday(&now, 0);
  462. printf("%ld.%06ld: select returned: %d, %s\n",
  463. (long)now.tv_sec, (long)now.tv_usec,
  464. res,
  465. res < 0 ? strerror(errno) : "success");
  466. if (res > 0) {
  467. if (FD_ISSET(sock, &readfs))
  468. printf("ready for reading\n");
  469. if (FD_ISSET(sock, &errorfs))
  470. printf("has error\n");
  471. recvpacket(sock, 0,
  472. siocgstamp,
  473. siocgstampns);
  474. recvpacket(sock, MSG_ERRQUEUE,
  475. siocgstamp,
  476. siocgstampns);
  477. }
  478. } else {
  479. /* write one packet */
  480. sendpacket(sock,
  481. (struct sockaddr *)&addr,
  482. sizeof(addr));
  483. next.tv_sec += 5;
  484. continue;
  485. }
  486. }
  487. return 0;
  488. }