2 * This file is part of the nqptp distribution (https://github.com/mikebrady/nqptp).
3 * Copyright (c) 2021 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.
20 #include "nqptp-clock-sources.h"
22 #include "nqptp-ptp-definitions.h"
23 #include <arpa/inet.h>
26 #include <sys/types.h>
29 #define FIELD_SIZEOF(t, f) (sizeof(((t *)0)->f))
32 clock_source_private_data clocks_private
[MAX_CLOCKS
];
34 int find_clock_source_record(char *sender_string
, clock_source_private_data
*clocks_private_info
) {
35 // return the index of the clock in the clock information arrays or -1
39 while ((found
== 0) && (i
< MAX_CLOCKS
)) {
40 if ((clocks_private_info
[i
].in_use
!= 0) &&
41 (strcasecmp(sender_string
, (const char *)&clocks_private_info
[i
].ip
) == 0))
51 int create_clock_source_record(char *sender_string
,
52 clock_source_private_data
*clocks_private_info
) {
53 // sometimes, the mutex will already be locked
54 // return the index of a clock entry in the clock information arrays or -1 if full
55 // initialise the entries in the shared and private arrays
59 while ((found
== 0) && (i
< MAX_CLOCKS
)) {
60 if (clocks_private_info
[i
].in_use
== 0)
68 memset(&clocks_private_info
[i
], 0, sizeof(clock_source_private_data
));
69 strncpy((char *)&clocks_private_info
[i
].ip
, sender_string
,
70 FIELD_SIZEOF(clock_source_private_data
, ip
) - 1);
71 clocks_private_info
[i
].in_use
= 1;
72 clocks_private_info
[i
].t2
= 0;
73 clocks_private_info
[i
].current_stage
= waiting_for_sync
;
74 clocks_private_info
[i
].vacant_samples
= MAX_TIMING_SAMPLES
;
75 debug(2, "create record for ip: %s.", &clocks_private_info
[i
].ip
);
77 die("Clock tables full!");
82 void manage_clock_sources(uint64_t reception_time
, clock_source_private_data
*clocks_private_info
) {
83 debug(3, "manage_clock_sources");
86 // do a garbage collect for clock records no longer in use
87 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
88 // only if its in use and not a timing peer... don't need a mutex to check
89 if ((clocks_private_info
[i
].in_use
!= 0) &&
90 ((clocks_private_info
[i
].flags
& (1 << clock_is_a_timing_peer
)) == 0)) {
91 int64_t time_since_last_use
= reception_time
- clocks_private_info
[i
].time_of_last_use
;
92 // using a sync timeout to determine when to drop the record...
93 // the following give the sync receipt time in whole seconds
94 // depending on the aPTPinitialLogSyncInterval and the aPTPsyncReceiptTimeout
95 int64_t syncTimeout
= (1 << (32 + aPTPinitialLogSyncInterval
));
96 syncTimeout
= syncTimeout
* aPTPsyncReceiptTimeout
;
97 syncTimeout
= syncTimeout
>> 32;
98 // seconds to nanoseconds
99 syncTimeout
= syncTimeout
* 1000000000;
100 if (time_since_last_use
> syncTimeout
) {
101 uint32_t old_flags
= clocks_private_info
[i
].flags
;
102 debug(2, "delete record for: %s.", &clocks_private_info
[i
].ip
);
103 memset(&clocks_private_info
[i
], 0, sizeof(clock_source_private_data
));
111 // check all the entries in the clock array and mark all those that
112 // belong to ourselves
114 void update_clock_self_identifications(clock_source_private_data
*clocks_private_info
) {
115 // first, turn off all the self-id flags
117 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
118 clocks_private_info
[i
].is_one_of_ours
= 0;
121 struct ifaddrs
*ifap
, *ifa
;
125 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
126 family
= ifa
->ifa_addr
->sa_family
;
128 if (ifa
->ifa_addr
&& family
== AF_INET6
) {
129 struct sockaddr_in6
*sa6
= (struct sockaddr_in6
*)ifa
->ifa_addr
;
130 addr
= &(sa6
->sin6_addr
);
133 if (ifa
->ifa_addr
&& family
== AF_INET
) {
134 struct sockaddr_in
*sa4
= (struct sockaddr_in
*)ifa
->ifa_addr
;
135 addr
= &(sa4
->sin_addr
);
138 memset(ip_string
, 0, sizeof(ip_string
));
140 inet_ntop(family
, addr
, ip_string
, sizeof(ip_string
));
141 if (strlen(ip_string
) != 0) {
142 // now set the is_one_of_ours flag of any clock with this ip
143 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
144 if (strcasecmp(ip_string
, clocks_private_info
[i
].ip
) == 0) {
145 debug(2, "found an entry for one of our clocks");
146 clocks_private_info
[i
].is_one_of_ours
= 1;
154 void update_master() {
156 // find the current master clock if there is one and turn off all mastership
158 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
159 if ((clocks_private
[i
].flags
& (1 << clock_is_master
)) != 0)
160 if (old_master
== -1)
161 old_master
= i
; // find old master
162 clocks_private
[i
].flags
&= ~(1 << clock_is_master
); // turn them all off
165 int best_so_far
= -1;
166 int timing_peer_count
= 0;
167 uint32_t acceptance_mask
=
168 (1 << clock_is_valid
) | (1 << clock_is_qualified
) | (1 << clock_is_a_timing_peer
);
169 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
170 if ((clocks_private
[i
].flags
& acceptance_mask
) == acceptance_mask
) {
171 // found a possible clock candidate
173 if (best_so_far
== -1) {
176 // do the data set comparison detailed in Figure 27 and Figure 28 on pp89-90
177 if (clocks_private
[i
].grandmasterIdentity
==
178 clocks_private
[best_so_far
].grandmasterIdentity
) {
179 // should implement Figure 28 here
180 } else if (clocks_private
[i
].grandmasterPriority1
<
181 clocks_private
[best_so_far
].grandmasterPriority1
) {
183 } else if (clocks_private
[i
].grandmasterClass
<
184 clocks_private
[best_so_far
].grandmasterClass
) {
186 } else if (clocks_private
[i
].grandmasterAccuracy
<
187 clocks_private
[best_so_far
].grandmasterAccuracy
) {
189 } else if (clocks_private
[i
].grandmasterVariance
<
190 clocks_private
[best_so_far
].grandmasterVariance
) {
192 } else if (clocks_private
[i
].grandmasterPriority2
<
193 clocks_private
[best_so_far
].grandmasterPriority2
) {
195 } else if (clocks_private
[i
].grandmasterIdentity
<
196 clocks_private
[best_so_far
].grandmasterIdentity
) {
202 if (best_so_far
== -1) {
204 if (old_master
!= -1) {
205 // but there was a master clock, so remove it
206 debug(1, "shm interface -- remove master clock designation");
207 update_master_clock_info(0, NULL
, 0, 0);
209 if (timing_peer_count
== 0)
210 debug(2, "No timing peer list found");
212 debug(1, "No master clock not found!");
214 // we found a master clock
215 clocks_private
[best_so_far
].flags
|= (1 << clock_is_master
);
216 // master_clock_index = best_so_far;
217 if (old_master
!= best_so_far
) {
218 update_master_clock_info(clocks_private
[best_so_far
].clock_id
,
219 (const char *)&clocks_private
[best_so_far
].ip
,
220 clocks_private
[best_so_far
].local_time
,
221 clocks_private
[best_so_far
].local_to_source_time_offset
);
225 int records_in_use
= 0;
226 for (i
= 0; i
< MAX_CLOCKS
; i
++)
227 if (clocks_private
[i
].in_use
!= 0)
229 if (records_in_use
> 0) {
231 debug(1, "Current NQPTP Status:");
232 uint32_t peer_mask
= (1 << clock_is_a_timing_peer
);
233 uint32_t peer_clock_mask
= peer_mask
| (1 << clock_is_valid
);
234 uint32_t peer_master_mask
= peer_clock_mask
| (1 << clock_is_master
);
235 uint32_t non_peer_clock_mask
= (1 << clock_is_valid
);
236 uint32_t non_peer_master_mask
= non_peer_clock_mask
| (1 << clock_is_master
);
237 for (i
= 0; i
< MAX_CLOCKS
; i
++) {
238 if (clocks_private
[i
].in_use
!= 0) {
239 if ((clocks_private
[i
].flags
& peer_master_mask
) == peer_master_mask
) {
240 debug(1, " Peer Master: %" PRIx64
" %s.", clocks_private
[i
].clock_id
,
241 clocks_private
[i
].ip
);
242 } else if ((clocks_private
[i
].flags
& peer_clock_mask
) == peer_clock_mask
) {
243 debug(1, " Peer Clock: %" PRIx64
" %s.", clocks_private
[i
].clock_id
,
244 clocks_private
[i
].ip
);
245 } else if ((clocks_private
[i
].flags
& peer_mask
) == peer_mask
) {
246 debug(1, " Peer: %s.", clocks_private
[i
].ip
);
247 } else if ((clocks_private
[i
].flags
& non_peer_master_mask
) == non_peer_master_mask
) {
248 debug(1, " Non Peer Master: %" PRIx64
" %s.", clocks_private
[i
].clock_id
,
249 clocks_private
[i
].ip
);
250 } else if ((clocks_private
[i
].flags
& non_peer_clock_mask
) == non_peer_clock_mask
) {
251 debug(1, " Non Peer Clock: %" PRIx64
" %s.", clocks_private
[i
].clock_id
,
252 clocks_private
[i
].ip
);
254 debug(1, " Non Peer Record: %s.", clocks_private
[i
].ip
);