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