2 * This file is part of the nqptp distribution (https://github.com/mikebrady/nqptp).
3 * Copyright (c) 2021-2022 Mike Brady.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 2.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Commercial licensing is also available.
22 #include "general-utilities.h"
23 #include "nqptp-clock-sources.h"
24 #include "nqptp-message-handlers.h"
25 #include "nqptp-ptp-definitions.h"
26 #include "nqptp-utilities.h"
28 #ifdef CONFIG_USE_GIT_VERSION_STRING
29 #include "gitversion.h"
32 #include <arpa/inet.h> // inet_ntop
33 #include <stdio.h> // fprint
34 #include <stdlib.h> // malloc;
35 #include <string.h> // memset
38 #include <unistd.h> // close
40 #include <fcntl.h> /* For O_* constants */
41 #include <sys/mman.h> // for shared memory stuff
42 #include <sys/select.h> // for fd_set
43 #include <sys/stat.h> // umask
44 #include <time.h> // for timeval
46 #include <signal.h> // SIGTERM and stuff like that
49 #include <sys/socket.h>
51 #ifdef CONFIG_FOR_FREEBSD
52 #include <netinet/in.h>
53 #include <sys/socket.h>
57 #define FIELD_SIZEOF(t, f) (sizeof(((t *)0)->f))
60 // 8 samples per second
62 #define BUFLEN 4096 // Max length of buffer
64 sockets_open_bundle sockets_open_stuff
;
67 uint64_t trigger_time
;
68 uint64_t (*task
)(uint64_t nominal_call_time
, void *private_data
);
74 timed_task_t timed_tasks
[TIMED_TASKS
];
77 uint64_t sample_task(uint64_t call_time, __attribute__((unused)) void *private_data) {
78 debug(1,"sample_task called.");
79 uint64_t next_time = call_time;
80 next_time = next_time + 1000000000;
88 // close any open sockets
90 for (i
= 0; i
< sockets_open_stuff
.sockets_open
; i
++)
91 close(sockets_open_stuff
.sockets
[i
].number
);
93 // close off shared memory interface
98 if (munmap(shared_memory
, sizeof(struct shm_structure
)) != 0) {
99 debug(1, "error unmapping shared memory \"%s\": \"%s\".", NQPTP_INTERFACE_NAME
, strerror(errno
));
102 if (shm_unlink(NQPTP_INTERFACE_NAME
) == -1) {
103 debug(1, "error unlinking shared memory \"%s\": \"%s\".", NQPTP_INTERFACE_NAME
, strerror(errno
));
115 void intHandler(__attribute__((unused
)) int k
) {
116 debug(1, "exit on SIGINT");
120 void termHandler(__attribute__((unused
)) int k
) {
121 debug(1, "exit on SIGTERM");
125 int main(int argc
, char **argv
) {
129 for (i
= 1; i
< argc
; ++i
) {
130 if (argv
[i
][0] == '-') {
131 if (strcmp(argv
[i
] + 1, "V") == 0) {
132 #ifdef CONFIG_USE_GIT_VERSION_STRING
133 if (git_version_string
[0] != '\0')
134 fprintf(stdout
, "Version: %s. Shared Memory Interface Version: %u.\n", git_version_string
,
135 NQPTP_SHM_STRUCTURES_VERSION
);
139 fprintf(stdout
, "Version: %s. Shared Memory Interface Version: %u.\n", VERSION
,
140 NQPTP_SHM_STRUCTURES_VERSION
);
142 } else if (strcmp(argv
[i
] + 1, "vvv") == 0) {
144 } else if (strcmp(argv
[i
] + 1, "vv") == 0) {
146 } else if (strcmp(argv
[i
] + 1, "v") == 0) {
148 } else if (strcmp(argv
[i
] + 1, "h") == 0) {
149 fprintf(stdout
, " -V print version,\n"
151 " -vv more verbose log,\n"
152 " -vvv very verbose log,\n"
153 " -h this help text.\n");
156 fprintf(stdout
, "%s -- unknown option. Program terminated.\n", argv
[0]);
162 debug_init(debug_level
, 0, 1, 1);
164 #ifdef CONFIG_USE_GIT_VERSION_STRING
165 if (git_version_string
[0] != '\0')
166 debug(1, "Version: %s, SMI: %u. Clock ID: \"%" PRIx64
"\".", git_version_string
,
167 NQPTP_SHM_STRUCTURES_VERSION
, get_self_clock_id());
170 debug(1, "Version: %s, SMI: %u. Clock ID: \"%" PRIx64
"\".", VERSION
,
171 NQPTP_SHM_STRUCTURES_VERSION
, get_self_clock_id());
173 // debug(1, "size of a clock entry is %u bytes.", sizeof(clock_source_private_data));
176 sockets_open_stuff
.sockets_open
= 0;
180 // control-c (SIGINT) cleanly
181 struct sigaction act
;
182 memset(&act
, 0, sizeof(struct sigaction
));
183 act
.sa_handler
= intHandler
;
184 sigaction(SIGINT
, &act
, NULL
);
186 // terminate (SIGTERM)
187 struct sigaction act2
;
188 memset(&act2
, 0, sizeof(struct sigaction
));
189 act2
.sa_handler
= termHandler
;
190 sigaction(SIGTERM
, &act2
, NULL
);
194 pthread_mutexattr_t shared
;
199 mode_t oldumask
= umask(0);
200 shm_fd
= shm_open(NQPTP_INTERFACE_NAME
, O_RDWR
| O_CREAT
, 0666);
202 die("cannot open shared memory \"%s\".", NQPTP_INTERFACE_NAME
);
204 (void)umask(oldumask
);
206 if (ftruncate(shm_fd
, sizeof(struct shm_structure
)) == -1) {
207 die("failed to set size of shared memory \"%s\".", NQPTP_INTERFACE_NAME
);
210 #ifdef CONFIG_FOR_FREEBSD
211 shared_memory
= (struct shm_structure
*)mmap(NULL
, sizeof(struct shm_structure
),
212 PROT_READ
| PROT_WRITE
, MAP_SHARED
, shm_fd
, 0);
215 #ifdef CONFIG_FOR_LINUX
217 (struct shm_structure
*)mmap(NULL
, sizeof(struct shm_structure
), PROT_READ
| PROT_WRITE
,
218 MAP_LOCKED
| MAP_SHARED
, shm_fd
, 0);
221 if (shared_memory
== (struct shm_structure
*)-1) {
222 die("failed to mmap shared memory \"%s\".", NQPTP_INTERFACE_NAME
);
226 warn("error closing \"%s\" after mapping.", shm_fd
);
230 memset(shared_memory
, 0, sizeof(struct shm_structure
));
231 shared_memory
->version
= NQPTP_SHM_STRUCTURES_VERSION
;
233 /*create mutex attr */
234 err
= pthread_mutexattr_init(&shared
);
236 die("mutex attribute initialization failed - %s.", strerror(errno
));
238 pthread_mutexattr_setpshared(&shared
, 1);
240 err
= pthread_mutex_init((pthread_mutex_t
*)&shared_memory
->shm_mutex
, &shared
);
242 die("mutex initialization failed - %s.", strerror(errno
));
245 err
= pthread_mutexattr_destroy(&shared
);
247 die("mutex attribute destruction failed - %s.", strerror(errno
));
254 // open sockets 319 and 320
256 open_sockets_at_port(NULL
, 319, &sockets_open_stuff
);
257 open_sockets_at_port(NULL
, 320, &sockets_open_stuff
);
258 open_sockets_at_port("localhost", NQPTP_CONTROL_PORT
,
259 &sockets_open_stuff
); // this for messages from the client
261 // start the timed tasks
262 uint64_t broadcasting_task(uint64_t call_time
, void *private_data
);
264 timed_tasks
[0].trigger_time
= get_time_now() + 100000000; // start after 100 ms
265 timed_tasks
[0].private_data
= (void *)&clocks_private
;
266 timed_tasks
[0].task
= broadcasting_task
;
268 // now, get down to business
269 if (sockets_open_stuff
.sockets_open
> 0) {
273 struct timeval timeout
;
274 FD_ZERO(&readSockSet
);
277 for (s
= 0; s
< sockets_open_stuff
.sockets_open
; s
++) {
278 if (sockets_open_stuff
.sockets
[s
].number
> smax
)
279 smax
= sockets_open_stuff
.sockets
[s
].number
;
280 FD_SET(sockets_open_stuff
.sockets
[s
].number
, &readSockSet
);
284 timeout
.tv_usec
= 10000; // timeout after ten milliseconds
285 int retval
= select(smax
+ 1, &readSockSet
, NULL
, NULL
, &timeout
);
286 uint64_t reception_time
= get_time_now(); // use this if other methods fail
289 for (t
= 0; t
< sockets_open_stuff
.sockets_open
; t
++) {
290 int socket_number
= sockets_open_stuff
.sockets
[t
].number
;
291 if (FD_ISSET(socket_number
, &readSockSet
)) {
293 SOCKADDR from_sock_addr
;
294 memset(&from_sock_addr
, 0, sizeof(SOCKADDR
));
303 memset(iov
, 0, sizeof(iov
));
304 memset(&msg
, 0, sizeof(msg
));
305 memset(&control
, 0, sizeof(control
));
307 iov
[0].iov_base
= buf
;
308 iov
[0].iov_len
= BUFLEN
;
313 msg
.msg_name
= &from_sock_addr
;
314 msg
.msg_namelen
= sizeof(from_sock_addr
);
315 msg
.msg_control
= &control
;
316 msg
.msg_controllen
= sizeof(control
);
318 uint16_t receiver_port
= 0;
319 // int msgsize = recv(udpsocket_fd, &msg_buffer, 4, 0);
320 recv_len
= recvmsg(socket_number
, &msg
, MSG_DONTWAIT
);
322 if (recv_len
!= -1) {
323 // get the receiver port
325 for (jp
= 0; jp
< sockets_open_stuff
.sockets_open
; jp
++) {
326 if (socket_number
== sockets_open_stuff
.sockets
[jp
].number
)
327 receiver_port
= sockets_open_stuff
.sockets
[jp
].port
;
330 if (recv_len
== -1) {
331 if (errno
== EAGAIN
) {
332 usleep(1000); // this can happen, it seems...
334 debug(1, "recvmsg() error %d", errno
);
336 // check if it's a control port message before checking for the length of the
338 } else if (receiver_port
== NQPTP_CONTROL_PORT
) {
339 handle_control_port_messages(
340 buf
, recv_len
, (clock_source_private_data
*)&clocks_private
, reception_time
);
341 } else if (recv_len
>= (ssize_t
)sizeof(struct ptp_common_message_header
)) {
342 debug_print_buffer(2, buf
, recv_len
);
344 // check its credentials
345 // the sending and receiving ports must be the same (i.e. 319 -> 319 or 320 -> 320)
347 // initialise the connection info
348 void *sender_addr
= NULL
;
349 uint16_t sender_port
= 0;
351 sa_family_t connection_ip_family
= from_sock_addr
.SAFAMILY
;
354 if (connection_ip_family
== AF_INET6
) {
355 struct sockaddr_in6
*sa6
= (struct sockaddr_in6
*)&from_sock_addr
;
356 sender_addr
= &(sa6
->sin6_addr
);
357 sender_port
= ntohs(sa6
->sin6_port
);
360 if (connection_ip_family
== AF_INET
) {
361 struct sockaddr_in
*sa4
= (struct sockaddr_in
*)&from_sock_addr
;
362 sender_addr
= &(sa4
->sin_addr
);
363 sender_port
= ntohs(sa4
->sin_port
);
366 if (sender_port
== receiver_port
) {
368 char sender_string
[256];
369 memset(sender_string
, 0, sizeof(sender_string
));
370 inet_ntop(connection_ip_family
, sender_addr
, sender_string
, sizeof(sender_string
));
371 // now, find the record for this ip
372 int the_clock
= find_clock_source_record(
373 sender_string
, (clock_source_private_data
*)&clocks_private
);
374 // not sure about requiring a Sync before creating it...
375 // if ((the_clock == -1) && ((buf[0] & 0xF) == Sync)) {
377 if (the_clock == -1) {
378 the_clock = create_clock_source_record(
379 sender_string, (clock_source_private_data *)&clocks_private);
382 if (the_clock
!= -1) {
383 clocks_private
[the_clock
].time_of_last_use
=
384 reception_time
; // for garbage collection
385 switch (buf
[0] & 0xF) {
387 handle_announce(buf
, recv_len
, &clocks_private
[the_clock
], reception_time
);
390 handle_follow_up(buf
, recv_len
, &clocks_private
[the_clock
], reception_time
);
393 handle_sync(buf
, recv_len
, &clocks_private
[the_clock
], reception_time
);
396 debug_print_buffer(2, buf
,
397 recv_len
); // unusual messages will have debug level 1.
400 } // otherwise, just forget it
407 // manage_clock_sources(reception_time, (clock_source_private_data *)&clocks_private);
409 for (i
= 0; i
< TIMED_TASKS
; i
++) {
410 if (timed_tasks
[i
].trigger_time
!= 0) {
411 int64_t time_to_wait
= timed_tasks
[i
].trigger_time
- reception_time
;
412 if (time_to_wait
<= 0) {
413 timed_tasks
[i
].trigger_time
=
414 timed_tasks
[i
].task(reception_time
, timed_tasks
[i
].private_data
);
420 // should never get to here, unless no sockets were ever opened.
424 void send_awakening_announcement_sequence(const uint64_t clock_id
, const char *clock_ip
,
425 const int ip_family
, const uint8_t priority1
,
426 const uint8_t priority2
) {
427 struct ptp_announce_message
*msg
;
428 size_t msg_length
= sizeof(struct ptp_announce_message
);
429 msg
= malloc(msg_length
);
430 memset((void *)msg
, 0, msg_length
);
432 uint64_t my_clock_id
= get_self_clock_id();
433 msg
->header
.transportSpecificAndMessageID
= 0x10 + Announce
;
434 msg
->header
.reservedAndVersionPTP
= 0x02;
435 msg
->header
.messageLength
= htons(sizeof(struct ptp_announce_message
));
436 msg
->header
.flags
= htons(0x0408);
437 hcton64(my_clock_id
, &msg
->header
.clockIdentity
[0]);
438 msg
->header
.sourcePortID
= htons(32776);
439 msg
->header
.controlField
= 0x05;
440 msg
->header
.logMessagePeriod
= 0xFE;
441 msg
->announce
.currentUtcOffset
= htons(37);
442 hcton64(my_clock_id
, &msg
->announce
.grandmasterIdentity
[0]);
443 uint32_t my_clock_quality
= 0xf8fe436a;
444 msg
->announce
.grandmasterClockQuality
= htonl(my_clock_quality
);
446 msg
->announce
.grandmasterPriority1
=
447 priority1
- 1; // make this announcement seem better than the clock we are about to ping
448 msg
->announce
.grandmasterPriority2
= priority2
;
450 warn("Cannot select a suitable priority for pinging clock %" PRIx64
" at %s.", clock_id
,
452 msg
->announce
.grandmasterPriority1
= 248;
453 msg
->announce
.grandmasterPriority2
= 248;
455 msg
->announce
.timeSource
= 160; // Internal Oscillator
457 // get the socket for the correct port -- 320 -- and family -- IPv4 or IPv6 -- to send it
462 for (t
= 0; t
< sockets_open_stuff
.sockets_open
; t
++) {
463 if ((sockets_open_stuff
.sockets
[t
].port
== 320) &&
464 (sockets_open_stuff
.sockets
[t
].family
== ip_family
))
465 s
= sockets_open_stuff
.sockets
[t
].number
;
468 debug(1, "sending socket not found for clock %" PRIx64
" at %s, family %s.", clock_id
, clock_ip
,
469 ip_family
== AF_INET
? "IPv4"
470 : ip_family
== AF_INET6
? "IPv6"
473 // debug(1, "Send message from socket %d.", s);
475 const char *portname
= "320";
476 struct addrinfo hints
;
477 memset(&hints
, 0, sizeof(hints
));
478 hints
.ai_family
= AF_UNSPEC
;
479 hints
.ai_socktype
= SOCK_DGRAM
;
480 hints
.ai_protocol
= 0;
481 hints
.ai_flags
= AI_ADDRCONFIG
;
482 struct addrinfo
*res
= NULL
;
483 int err
= getaddrinfo(clock_ip
, portname
, &hints
, &res
);
485 debug(1, "failed to resolve remote socket address (err=%d)", err
);
487 // here, we have the destination, so send it
489 // debug_print_buffer(1, (char *)msg, msg_length);
490 int ret
= sendto(s
, msg
, msg_length
, 0, res
->ai_addr
, res
->ai_addrlen
);
492 debug(1, "result of sendto is %d.", ret
);
493 debug(2, "Send awaken Announce message to clock \"%" PRIx64
"\" at %s on %s.", clock_id
,
494 clock_ip
, ip_family
== AF_INET6
? "IPv6" : "IPv4");
496 if (priority1
< 254) {
497 msg
->announce
.grandmasterPriority1
=
498 priority1
+ 1; // make this announcement seem worse than the clock we about to ping
500 warn("Cannot select a suitable priority for second ping of clock %" PRIx64
" at %s.",
502 msg
->announce
.grandmasterPriority1
= 250;
505 msg
->announce
.grandmasterPriority2
= priority2
;
507 ret
= sendto(s
, msg
, msg_length
, 0, res
->ai_addr
, res
->ai_addrlen
);
509 debug(1, "result of second sendto is %d.", ret
);
516 uint64_t broadcasting_task(uint64_t call_time
, __attribute__((unused
)) void *private_data
) {
517 clock_source_private_data
*clocks_private
= (clock_source_private_data
*)private_data
;
519 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
525 for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++)
526 if ((clocks_private->client_flags[temp_client_id] & (1 << clock_is_master)) != 0)
528 // only process it if it's a master somewhere...
529 if ((is_a_master != 0) && (clocks_private[i].announcements_without_followups == 3)) {
531 if (clocks_private
[i
].announcements_without_followups
== 3) {
532 if (clocks_private
[i
].follow_up_number
== 0) {
534 "Attempt to awaken a silent clock %" PRIx64
535 ", index %u, at follow_up_number %u at IP %s.",
536 clocks_private
[i
].clock_id
, i
, clocks_private
[i
].follow_up_number
,
537 clocks_private
[i
].ip
);
539 // send an Announce message to attempt to waken this silent PTP clock by
540 // getting it to negotiate with an apparently better clock
541 // that then immediately sends another Announce message indicating that it's inferior
543 clocks_private
[i
].announcements_without_followups
++; // set to 4 to indicate done/parked
544 send_awakening_announcement_sequence(
545 clocks_private
[i
].clock_id
, clocks_private
[i
].ip
, clocks_private
[i
].family
,
546 clocks_private
[i
].grandmasterPriority1
, clocks_private
[i
].grandmasterPriority2
);
549 "Silent clock %" PRIx64
550 " detected, index %u, at follow_up_number %u at IP %s. No attempt to awaken it.",
551 clocks_private
[i
].clock_id
, i
, clocks_private
[i
].follow_up_number
,
552 clocks_private
[i
].ip
);
558 uint64_t announce_interval = 1;
559 announce_interval = announce_interval << (8 + aPTPinitialLogAnnounceInterval);
560 announce_interval = announce_interval * 1000000000;
561 announce_interval = announce_interval >> 8; // nanoseconds
562 return call_time + announce_interval;
564 return call_time
+ 50000000;