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