timestamping.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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. if (argc < 2)
  310. usage(0);
  311. interface = argv[1];
  312. for (i = 2; i < argc; i++) {
  313. if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
  314. so_timestamp = 1;
  315. else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
  316. so_timestampns = 1;
  317. else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
  318. siocgstamp = 1;
  319. else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
  320. siocgstampns = 1;
  321. else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
  322. ip_multicast_loop = 1;
  323. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
  324. so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
  325. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
  326. so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
  327. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
  328. so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
  329. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
  330. so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
  331. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
  332. so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
  333. else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
  334. so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
  335. else
  336. usage(argv[i]);
  337. }
  338. sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  339. if (sock < 0)
  340. bail("socket");
  341. memset(&device, 0, sizeof(device));
  342. strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
  343. if (ioctl(sock, SIOCGIFADDR, &device) < 0)
  344. bail("getting interface IP address");
  345. memset(&hwtstamp, 0, sizeof(hwtstamp));
  346. strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
  347. hwtstamp.ifr_data = (void *)&hwconfig;
  348. memset(&hwconfig, 0, sizeof(hwconfig));
  349. hwconfig.tx_type =
  350. (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
  351. HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
  352. hwconfig.rx_filter =
  353. (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
  354. HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
  355. hwconfig_requested = hwconfig;
  356. if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
  357. if ((errno == EINVAL || errno == ENOTSUP) &&
  358. hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
  359. hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
  360. printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
  361. else
  362. bail("SIOCSHWTSTAMP");
  363. }
  364. printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
  365. hwconfig_requested.tx_type, hwconfig.tx_type,
  366. hwconfig_requested.rx_filter, hwconfig.rx_filter);
  367. /* bind to PTP port */
  368. addr.sin_family = AF_INET;
  369. addr.sin_addr.s_addr = htonl(INADDR_ANY);
  370. addr.sin_port = htons(319 /* PTP event port */);
  371. if (bind(sock,
  372. (struct sockaddr *)&addr,
  373. sizeof(struct sockaddr_in)) < 0)
  374. bail("bind");
  375. /* set multicast group for outgoing packets */
  376. inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
  377. addr.sin_addr = iaddr;
  378. imr.imr_multiaddr.s_addr = iaddr.s_addr;
  379. imr.imr_interface.s_addr =
  380. ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
  381. if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
  382. &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
  383. bail("set multicast");
  384. /* join multicast group, loop our own packet */
  385. if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
  386. &imr, sizeof(struct ip_mreq)) < 0)
  387. bail("join multicast group");
  388. if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
  389. &ip_multicast_loop, sizeof(enabled)) < 0) {
  390. bail("loop multicast");
  391. }
  392. /* set socket options for time stamping */
  393. if (so_timestamp &&
  394. setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
  395. &enabled, sizeof(enabled)) < 0)
  396. bail("setsockopt SO_TIMESTAMP");
  397. if (so_timestampns &&
  398. setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
  399. &enabled, sizeof(enabled)) < 0)
  400. bail("setsockopt SO_TIMESTAMPNS");
  401. if (so_timestamping_flags &&
  402. setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
  403. &so_timestamping_flags,
  404. sizeof(so_timestamping_flags)) < 0)
  405. bail("setsockopt SO_TIMESTAMPING");
  406. /* request IP_PKTINFO for debugging purposes */
  407. if (setsockopt(sock, SOL_IP, IP_PKTINFO,
  408. &enabled, sizeof(enabled)) < 0)
  409. printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
  410. /* verify socket options */
  411. len = sizeof(val);
  412. if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
  413. printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
  414. else
  415. printf("SO_TIMESTAMP %d\n", val);
  416. if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
  417. printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
  418. strerror(errno));
  419. else
  420. printf("SO_TIMESTAMPNS %d\n", val);
  421. if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
  422. printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
  423. strerror(errno));
  424. } else {
  425. printf("SO_TIMESTAMPING %d\n", val);
  426. if (val != so_timestamping_flags)
  427. printf(" not the expected value %d\n",
  428. so_timestamping_flags);
  429. }
  430. /* send packets forever every five seconds */
  431. gettimeofday(&next, 0);
  432. next.tv_sec = (next.tv_sec + 1) / 5 * 5;
  433. next.tv_usec = 0;
  434. while (1) {
  435. struct timeval now;
  436. struct timeval delta;
  437. long delta_us;
  438. int res;
  439. fd_set readfs, errorfs;
  440. gettimeofday(&now, 0);
  441. delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
  442. (long)(next.tv_usec - now.tv_usec);
  443. if (delta_us > 0) {
  444. /* continue waiting for timeout or data */
  445. delta.tv_sec = delta_us / 1000000;
  446. delta.tv_usec = delta_us % 1000000;
  447. FD_ZERO(&readfs);
  448. FD_ZERO(&errorfs);
  449. FD_SET(sock, &readfs);
  450. FD_SET(sock, &errorfs);
  451. printf("%ld.%06ld: select %ldus\n",
  452. (long)now.tv_sec, (long)now.tv_usec,
  453. delta_us);
  454. res = select(sock + 1, &readfs, 0, &errorfs, &delta);
  455. gettimeofday(&now, 0);
  456. printf("%ld.%06ld: select returned: %d, %s\n",
  457. (long)now.tv_sec, (long)now.tv_usec,
  458. res,
  459. res < 0 ? strerror(errno) : "success");
  460. if (res > 0) {
  461. if (FD_ISSET(sock, &readfs))
  462. printf("ready for reading\n");
  463. if (FD_ISSET(sock, &errorfs))
  464. printf("has error\n");
  465. recvpacket(sock, 0,
  466. siocgstamp,
  467. siocgstampns);
  468. recvpacket(sock, MSG_ERRQUEUE,
  469. siocgstamp,
  470. siocgstampns);
  471. }
  472. } else {
  473. /* write one packet */
  474. sendpacket(sock,
  475. (struct sockaddr *)&addr,
  476. sizeof(addr));
  477. next.tv_sec += 5;
  478. continue;
  479. }
  480. }
  481. return 0;
  482. }