]> git.ipfire.org Git - thirdparty/nqptp.git/blob - nqptp-message-handlers.c
Merge pull request #34 from heitbaum/patch-1
[thirdparty/nqptp.git] / nqptp-message-handlers.c
1 /*
2 * This file is part of the nqptp distribution (https://github.com/mikebrady/nqptp).
3 * Copyright (c) 2021-2022 Mike Brady.
4 *
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.
8 *
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.
13 *
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/>.
16 *
17 * Commercial licensing is also available.
18 */
19 #include <arpa/inet.h> // ntohl and ntohs
20 #include <string.h> //strsep
21
22 #include <stdio.h> // snprintf
23 #include <stdlib.h> // drand48
24 #include <unistd.h> // usleep
25
26 #include "debug.h"
27 #include "general-utilities.h"
28 #include "nqptp-message-handlers.h"
29 #include "nqptp-ptp-definitions.h"
30 #include "nqptp-utilities.h"
31
32 char hexcharbuffer[16384];
33 int reset_clock_smoothing = 0;
34 uint64_t clock_validity_expiration_time = 0;
35 int clock_is_active = 0;
36
37 char *hex_string(void *buf, size_t buf_len) {
38 char *tbuf = (char *)buf;
39 char *obfp = hexcharbuffer;
40 size_t obfc;
41 for (obfc = 0; obfc < buf_len; obfc++) {
42 snprintf(obfp, 3, "%02X", *tbuf);
43 obfp += 2;
44 tbuf = tbuf + 1;
45 };
46 *obfp = 0;
47 return hexcharbuffer;
48 }
49
50 void handle_control_port_messages(char *buf, ssize_t recv_len,
51 clock_source_private_data *clock_private_info,
52 uint64_t reception_time) {
53 if (recv_len != -1) {
54 if ((buf != NULL) && (recv_len > 0)) {
55 buf[recv_len - 1] = 0; // we know it's not empty, so make sure there's a null in it.
56 debug(2, "New control port message: \"%s\".", buf);
57 // we need to get the client shared memory interface name from the front
58 char *ip_list = buf;
59 char *smi_name = strsep(&ip_list, " ");
60 char *command = NULL;
61 if (smi_name != NULL) {
62 if (ip_list != NULL)
63 command = strsep(&ip_list, " ");
64
65 // "B" is for play begin/resume. Assumes a "T <ip>" already
66 // "E" is for play end/stop.
67 // "P" is for pause (currently Buffered Audio only).
68 //
69 // "T <ip>" is for the IP address of a timer.
70 // "T" means no active timer.
71 // clock_is_active is made true by Play and false by Pause or End.
72 if (command != NULL) {
73 if ((strcmp(command, "B") == 0) && (ip_list == NULL)) {
74 debug(2, "Play.");
75 // We want to avoid, as much as possible, resetting the clock smoothing.
76 // If we know the clock is already active or
77 // if it's only been a short time since we know it was last active
78 // then we will not reset the clock.
79 if (clock_is_active) {
80 debug(2, "clock is already active");
81 } else {
82 // Find out if the clock is active i.e. not sleeping.
83 // We know it is active between "B" and "E" commands.
84 // We also know it is active for brief periods after the "T" and "E" commands are
85 // received. If it is not definitely active, we will reset smoothing.
86 int will_ask_for_a_reset = 0;
87 if (clock_validity_expiration_time == 0) {
88 debug(1, "no clock_validity_expiration_time.");
89 will_ask_for_a_reset = 1;
90 } else {
91 int64_t time_to_clock_expiration = clock_validity_expiration_time - reception_time;
92 // timings obtained with an iPhone Xs Max on battery save
93
94 // around 30 seconds at a buffered audio pause on an iphone.
95 // around 1 second after a buffered audio stop on an iphone
96 // 10 seconds after a "T" from an iPhone that immediately sleeps
97 // more than a minute from "T" from a HomePod mini.
98
99 if (time_to_clock_expiration < 0) {
100 debug(2, "Clock validity may have expired, so ask for a reset.");
101 will_ask_for_a_reset = 1;
102 }
103 }
104 if (will_ask_for_a_reset != 0) {
105 debug(2, "Reset clock smoothing");
106 reset_clock_smoothing = 1;
107 }
108 }
109 clock_is_active = 1;
110 clock_validity_expiration_time = 0;
111 } else if ((strcmp(command, "E") == 0) && (ip_list == NULL)) {
112 debug(2, "Stop");
113 if (clock_is_active) {
114 debug(2, "reset clock_validity_expiration_time to 2.25 seconds in the future.");
115 clock_validity_expiration_time =
116 reception_time + 2250000000; // expiration time can be very soon after an "E"
117 clock_is_active = 0;
118 } else {
119 debug(2, "clock is already inactive.");
120 }
121 } else if ((strcmp(command, "P") == 0) && (ip_list == NULL)) {
122 debug(2, "Pause");
123 // A pause always seems to turn into a Stop in now more than a few seconds, and the
124 // clock keeps going, it seems so there is nothing to do here.
125 } else if ((command == NULL) || ((strcmp(command, "T") == 0) && (ip_list == NULL))) {
126 debug(2, "Stop Timing");
127 clock_is_active = 0;
128 debug(2, "Clear timing peer group.");
129 // dirty experimental hack -- delete all the clocks
130 int gc;
131 for (gc = 0; gc < MAX_CLOCKS; gc++) {
132 memset(&clock_private_info[gc], 0, sizeof(clock_source_private_data));
133 }
134 update_master_clock_info(0, NULL, 0, 0, 0); // the SMI may have obsolete stuff in it
135 } else {
136 debug(2, "Start Timing");
137 // dirty experimental hack -- delete all the clocks
138 int gc;
139 for (gc = 0; gc < MAX_CLOCKS; gc++) {
140 memset(&clock_private_info[gc], 0, sizeof(clock_source_private_data));
141 }
142 debug(2, "get or create new record for \"%s\".", smi_name);
143 // client_id = get_client_id(smi_name); // create the record if it doesn't exist
144 // if (client_id != -1) {
145 if (strcmp(command, "T") == 0) {
146 int i;
147 for (i = 0; i < MAX_CLOCKS; i++) {
148 clock_private_info[i].announcements_without_followups =
149 0; // to allow a possibly silent clock to be revisited when added to a timing
150 // peer list
151 clock_private_info[i].follow_up_number = 0;
152 }
153
154 // take the first ip and make it the master, permanently
155
156 if (ip_list != NULL) {
157 char *new_ip = strsep(&ip_list, " ");
158 // look for the IP in the list of clocks, and create an inert entry if not there
159 if ((new_ip != NULL) && (new_ip[0] != 0)) {
160 int t = find_clock_source_record(new_ip, clock_private_info);
161 if (t == -1)
162 t = create_clock_source_record(new_ip, clock_private_info);
163 if (t != -1) { // if the clock table is not full, okay
164 debug(2, "Monitor clock at %s.", new_ip);
165 }
166 // otherwise, drop it
167 }
168 }
169 // a new clock timing record will be started now
170 debug(2, "reset clock_validity_expiration_time to 5.0 seconds in the future.");
171 clock_validity_expiration_time =
172 reception_time + 5000000000L; // clock can stop as soon as 6 seconds after a "T"
173 } else {
174 warn("Unrecognised string on the control port.");
175 }
176 // } else {
177 // warn("Could not find or create a record for SMI Interface \"%s\".",
178 // smi_name);
179 // }
180 }
181 }
182 } else {
183 warn("SMI Interface Name not found on the control port.");
184 }
185 } else {
186 warn("Missing or empty packet on the control port.");
187 }
188 } else {
189 warn("Bad packet on the control port.");
190 }
191 }
192
193 void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
194 __attribute__((unused)) uint64_t reception_time) {
195 // debug_print_buffer(1, buf, (size_t) recv_len);
196 // make way for the new time
197 if ((size_t)recv_len >= sizeof(struct ptp_announce_message)) {
198 struct ptp_announce_message *msg = (struct ptp_announce_message *)buf;
199
200 uint64_t packet_clock_id = nctohl(&msg->header.clockIdentity[0]);
201 uint64_t packet_clock_id_low = nctohl(&msg->header.clockIdentity[4]);
202 packet_clock_id = packet_clock_id << 32;
203 packet_clock_id = packet_clock_id + packet_clock_id_low;
204 clock_private_info->clock_id = packet_clock_id;
205 clock_private_info->grandmasterPriority1 =
206 msg->announce.grandmasterPriority1; // need this for possibly pinging it later...
207 clock_private_info->grandmasterPriority2 =
208 msg->announce.grandmasterPriority2; // need this for possibly pinging it later...
209
210 debug(2, "announcement seen from %" PRIx64 " at %s.", clock_private_info->clock_id,
211 clock_private_info->ip);
212
213 if (clock_private_info->announcements_without_followups < 5) // don't keep going forever
214 // a value of 4 means it's parked --
215 // it has seen three, poked the clock and doesn't want to do any more.
216 clock_private_info->announcements_without_followups++;
217
218 uint64_t grandmaster_clock_id = nctohl(&msg->announce.grandmasterIdentity[0]);
219 uint64_t grandmaster_clock_id_low = nctohl(&msg->announce.grandmasterIdentity[4]);
220 grandmaster_clock_id = grandmaster_clock_id << 32;
221 grandmaster_clock_id = grandmaster_clock_id + grandmaster_clock_id_low;
222 uint32_t clockQuality = ntohl(msg->announce.grandmasterClockQuality);
223 uint8_t clockClass = (clockQuality >> 24) & 0xff;
224 uint8_t clockAccuracy = (clockQuality >> 16) & 0xff;
225 uint16_t offsetScaledLogVariance = clockQuality & 0xffff;
226 uint16_t stepsRemoved = ntohs(msg->announce.stepsRemoved);
227 uint16_t sourcePortID = ntohs(msg->header.sourcePortID);
228
229 clock_private_info->grandmasterIdentity = grandmaster_clock_id;
230 clock_private_info->grandmasterPriority1 = msg->announce.grandmasterPriority1;
231 clock_private_info->grandmasterQuality = clockQuality;
232 clock_private_info->grandmasterClass = clockClass;
233 clock_private_info->grandmasterAccuracy = clockAccuracy;
234 clock_private_info->grandmasterVariance = offsetScaledLogVariance;
235 clock_private_info->grandmasterPriority2 = msg->announce.grandmasterPriority2;
236 clock_private_info->stepsRemoved = stepsRemoved;
237 clock_private_info->clock_port_number = sourcePortID;
238
239 if (clock_private_info->wakeup_sent == 0) {
240 send_awakening_announcement_sequence(
241 clock_private_info->clock_id, clock_private_info->ip, clock_private_info->family,
242 clock_private_info->grandmasterPriority1, clock_private_info->grandmasterPriority2);
243 clock_private_info->wakeup_sent = 1;
244 }
245 }
246 }
247
248 void handle_sync(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
249 __attribute__((unused)) uint64_t reception_time) {
250 /*
251 // diagnostic -- decide whether to delay the processing of the follow_up to simulate a noisy
252 network if (drand48() < 0.015) {
253 // generate a random delay between 10 and 3500 milliseconds
254 int delay = (int)((3000 - 60) * drand48()) + 60;
255 debug(1,"Delay sync processing by %u milliseconds.", delay);
256 usleep(delay * 1000);
257 reception_time = get_time_now();
258 }
259 */
260
261 if (clock_private_info->clock_id == 0) {
262 debug(2, "Sync received before announcement -- discarded.");
263 } else {
264 if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_sync_message))) {
265 // debug_print_buffer(1, buf, recv_len);
266 struct ptp_sync_message *msg = (struct ptp_sync_message *)buf;
267
268 // clang-format off
269
270 // actually the precision timestamp needs to be corrected by the Follow_Up Correction_Field contents.
271 // According to IEEE Std 802.1AS-2020, paragraph 11.4.4.2.1:
272 /*
273 The value of the preciseOriginTimestamp field is the sourceTime of the ClockMaster entity of the Grandmaster PTP Instance,
274 when the associated Sync message was sent by that Grandmaster PTP Instance, with any fractional nanoseconds truncated (see 10.2.9).
275 The sum of the correctionFields in the Follow_Up and associated Sync messages, added to the preciseOriginTimestamp field of the Follow_Up message,
276 is the value of the synchronized time corresponding to the syncEventEgressTimestamp at the PTP Instance that sent the associated Sync message,
277 including any fractional nanoseconds.
278 */
279
280 // clang-format on
281
282 int64_t correction_field = ntoh64(msg->header.correctionField);
283
284 if (correction_field != 0)
285 debug(1, "Sync correction field is non-zero: %" PRId64 " ns.", correction_field);
286
287 correction_field = correction_field / 65536; // might be signed
288 } else {
289 debug(1, "Sync message is too small to be valid.");
290 }
291 }
292 }
293
294 void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
295 uint64_t reception_time) {
296 if (clock_private_info->clock_id == 0) {
297 debug(2, "Follow_Up received before announcement -- discarded.");
298 } else {
299 clock_private_info->announcements_without_followups = 0;
300 if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_follow_up_message))) {
301 // debug_print_buffer(1, buf, recv_len);
302 struct ptp_follow_up_message *msg = (struct ptp_follow_up_message *)buf;
303 uint16_t seconds_hi = nctohs(&msg->follow_up.preciseOriginTimestamp[0]);
304 uint32_t seconds_low = nctohl(&msg->follow_up.preciseOriginTimestamp[2]);
305 uint32_t nanoseconds = nctohl(&msg->follow_up.preciseOriginTimestamp[6]);
306 uint64_t preciseOriginTimestamp = seconds_hi;
307 preciseOriginTimestamp = preciseOriginTimestamp << 32;
308 preciseOriginTimestamp = preciseOriginTimestamp + seconds_low;
309 preciseOriginTimestamp = preciseOriginTimestamp * 1000000000L;
310 preciseOriginTimestamp = preciseOriginTimestamp + nanoseconds;
311
312 // update our sample information
313 int grandmasterClockIsStopped = 0;
314 if ((clock_private_info->previous_preciseOriginTimestamp == preciseOriginTimestamp) &&
315 (clock_private_info->clock_id == clock_private_info->grandmasterIdentity)) {
316 clock_private_info->identical_previous_preciseOriginTimestamp_count++;
317 grandmasterClockIsStopped = 1;
318 if (clock_private_info->identical_previous_preciseOriginTimestamp_count == 8 * 60) {
319 int64_t duration_of_mastership =
320 reception_time - clock_private_info->mastership_start_time;
321 if (clock_private_info->mastership_start_time == 0)
322 duration_of_mastership = 0;
323 debug(2,
324 "Clock %" PRIx64 "'s grandmaster clock has stopped after %f seconds of mastership.",
325 clock_private_info->clock_id, 0.000000001 * duration_of_mastership);
326 int64_t wait_limit = 62;
327 wait_limit = wait_limit * 1000000000;
328 // only try to restart a grandmaster clock on the clock itself.
329 if ((duration_of_mastership <= wait_limit) &&
330 (clock_private_info->clock_id == clock_private_info->grandmasterIdentity)) {
331 debug(2,
332 "Attempt to start a stopped clock %" PRIx64 ", at follow_up_number %u at IP %s.",
333 clock_private_info->clock_id, clock_private_info->follow_up_number,
334 clock_private_info->ip);
335 send_awakening_announcement_sequence(
336 clock_private_info->clock_id, clock_private_info->ip, clock_private_info->family,
337 clock_private_info->grandmasterPriority1, clock_private_info->grandmasterPriority2);
338 }
339 }
340 } else {
341 clock_private_info->identical_previous_preciseOriginTimestamp_count = 0;
342 }
343
344 clock_private_info->previous_preciseOriginTimestamp = preciseOriginTimestamp;
345
346 // clang-format off
347
348 // actually the precision timestamp needs to be corrected by the Follow_Up Correction_Field contents.
349 // According to IEEE Std 802.1AS-2020, paragraph 11.4.4.2.1:
350 /*
351 The value of the preciseOriginTimestamp field is the sourceTime of the ClockMaster entity of the Grandmaster PTP Instance,
352 when the associated Sync message was sent by that Grandmaster PTP Instance, with any fractional nanoseconds truncated (see 10.2.9).
353 The sum of the correctionFields in the Follow_Up and associated Sync messages, added to the preciseOriginTimestamp field of the Follow_Up message,
354 is the value of the synchronized time corresponding to the syncEventEgressTimestamp at the PTP Instance that sent the associated Sync message,
355 including any fractional nanoseconds.
356 */
357
358 // clang-format on
359
360 int64_t correction_field = ntoh64(msg->header.correctionField);
361
362 // debug(1," Check ntoh64: in: %" PRIx64 ", out: %" PRIx64 ".", msg->header.correctionField,
363 // correction_field);
364
365 correction_field = correction_field / 65536; // might be signed
366 uint64_t correctedPreciseOriginTimestamp = preciseOriginTimestamp + correction_field;
367
368 if (clock_private_info->follow_up_number < 100)
369 clock_private_info->follow_up_number++;
370
371 // if (clock_private_info->announcements_without_followups < 4) // if we haven't signalled
372 // already
373 clock_private_info->announcements_without_followups = 0; // we've seen a followup
374
375 debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id,
376 &clock_private_info->ip);
377 uint64_t offset = correctedPreciseOriginTimestamp - reception_time;
378
379 int64_t jitter = 0;
380
381 int64_t time_since_previous_offset = 0;
382 uint64_t smoothed_offset = offset;
383
384 // This is a bit hacky.
385 // Basically, the idea is that if the grandmaster has changed, then acceptance checking and
386 // smoothing should start as it it's a new clock. This is because the
387 // correctedPreciseOriginTimestamp, which is part of the data that is being smoothed, refers
388 // to the grandmaster, so when the grandmaster changes any previous calculations are no
389 // longer valid. The hacky bit is to signal this condition by zeroing the
390 // previous_offset_time.
391 if (clock_private_info->previous_offset_grandmaster !=
392 clock_private_info->grandmasterIdentity) {
393 clock_private_info->previous_offset_time = 0;
394 if (clock_private_info->previous_offset_grandmaster == 0)
395 debug(1, "grandmaster is %" PRIx64 ".", clock_private_info->grandmasterIdentity);
396 else
397 debug(1, "grandmaster has changed from %" PRIx64 " to %" PRIx64 ".",
398 clock_private_info->previous_offset_grandmaster,
399 clock_private_info->grandmasterIdentity);
400 }
401
402 // Do acceptance checking and smoothing.
403
404 // Positive changes in the offset are much more likely to be
405 // legitimate, since they could only occur due to a shorter
406 // propagation time or less of a delay sending or receiving the packet.
407 // When the clock is new, we give preferential weighting to
408 // positive changes in the offset to allow the clock to sync up quickly.
409
410 // If the new offset is greater, by any amount, than the old offset,
411 // or if it is less by up to the clamping_limit, accept it.
412
413 // This seems to be quite stable
414
415 if (reset_clock_smoothing == 0) {
416
417 if (clock_private_info->previous_offset_time != 0) {
418 time_since_previous_offset = reception_time - clock_private_info->previous_offset_time;
419 jitter = offset - clock_private_info->previous_offset;
420 }
421
422 // We take any positive or a limited negative jitter as a sync event in
423 // a continuous synchronisation sequence.
424
425 // The full value of a positive offset jitter is accepted for a
426 // number of follow_ups at the start.
427 // After that, the weight of the jitter is reduced.
428 // Follow-ups don't always come in at 125 ms intervals, especially after a discontinuity
429 // Delays makes the offsets smaller than they should be, which is quickly
430 // allowed for.
431
432 const int64_t clamping_limit = -2500000; // nanoseconds
433
434 int64_t mastership_time = reception_time - clock_private_info->mastership_start_time;
435 if (clock_private_info->mastership_start_time == 0)
436 mastership_time = 0;
437
438 // if ((clock_private_info->previous_offset_time != 0) &&
439 // (clock_private_info->identical_previous_preciseOriginTimestamp_count <= 1)) {
440 if (clock_private_info->previous_offset_time != 0) {
441 if (jitter < 0) {
442 int64_t clamped_jitter = jitter;
443 if (clamped_jitter < clamping_limit) {
444 clamped_jitter = clamping_limit; // 0 means ignore a clamped value completely
445 }
446 // if (mastership_time < 1000000000) // at the beginning, if jitter is negative
447 // smoothed_offset = clock_private_info->previous_offset + clamped_jitter / 16;
448 // else
449
450 // ignore negative jitter at first...
451 smoothed_offset = clock_private_info->previous_offset;
452 if (mastership_time > 1000000000)
453 smoothed_offset += clamped_jitter / 256; // later, if jitter is negative
454 } else if (mastership_time < 1000000000) { // at the beginning
455 smoothed_offset =
456 clock_private_info->previous_offset +
457 jitter /
458 1; // at the beginning, if jitter is positive -- accept positive changes quickly
459 } else {
460 smoothed_offset =
461 clock_private_info->previous_offset + jitter / 16; // later, if jitter is positive
462 }
463 } else {
464 if (clock_private_info->previous_offset_time == 0)
465 debug(2, "Clock %" PRIx64 " record (re)starting at %s.", clock_private_info->clock_id,
466 clock_private_info->ip);
467 else
468 debug(2,
469 "Timing discontinuity on clock %" PRIx64
470 " at %s: time_since_previous_offset: %.3f seconds%s.",
471 clock_private_info->clock_id, clock_private_info->ip,
472 0.000000001 * time_since_previous_offset,
473 grandmasterClockIsStopped != 0 ? ", grandmaster clock stopped" : "");
474 smoothed_offset = offset;
475 // clock_private_info->follow_up_number = 0;
476 clock_private_info->mastership_start_time =
477 reception_time; // mastership is reset to this time...
478 }
479
480 int64_t delta = smoothed_offset - offset;
481 debug(2,
482 "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
483 ", smoothed offset: %" PRIx64
484 ". Smoothed Offset - Offset: %10.3f. Raw Precise Origin Timestamp: %" PRIx64
485 "%s correction_field: %" PRIx64
486 ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
487 "%u. Source: %s",
488 clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
489 smoothed_offset, 0.000001 * delta, preciseOriginTimestamp,
490 clock_is_active != 0 ? ". " : "*.", correction_field,
491 0.000001 * time_since_previous_offset, ntohs(msg->header.sequenceId),
492 clock_private_info->follow_up_number, clock_private_info->ip);
493 if (clock_is_active) {
494 update_master_clock_info(clock_private_info->grandmasterIdentity,
495 (const char *)&clock_private_info->ip, reception_time,
496 smoothed_offset, clock_private_info->mastership_start_time);
497 } else {
498 update_master_clock_info(0, NULL, 0, 0, 0); // the SMI may have obsolete stuff in it
499 }
500
501 clock_private_info->previous_offset = smoothed_offset;
502 clock_private_info->previous_offset_time = reception_time;
503
504 } else {
505 reset_clock_smoothing = 0;
506 clock_private_info->mastership_start_time = 0;
507 clock_private_info->previous_offset = 0;
508 clock_private_info->previous_offset_time =
509 0; // so that the first non-stopped sample will be taken as the first one in a sequence
510 }
511
512 clock_private_info->previous_offset_grandmaster = clock_private_info->grandmasterIdentity;
513
514 // now do some quick calculations on the possible "Universal Time"
515 // debug_print_buffer(1, "", buf, recv_len);
516 uint8_t *tlv = (uint8_t *)&msg->follow_up.tlvs[0];
517 uint8_t *lastGmPhaseChange = tlv + 16;
518 uint64_t lpt = nctoh64(lastGmPhaseChange + 4);
519 uint64_t last_tlv_clock = nctoh64((uint8_t *)buf + 86);
520 uint64_t huh = offset - lpt;
521 debug_print_buffer(2, buf, (size_t)recv_len);
522 debug(2,
523 "%" PRIx64 ", %" PRIx64 ", %s, Origin: %016" PRIx64 ", LPT: %016" PRIx64
524 ", Offset: %016" PRIx64 ", Universal Offset: %016" PRIx64 ", packet length: %u.",
525 clock_private_info->clock_id, last_tlv_clock, hex_string(lastGmPhaseChange, 12),
526 preciseOriginTimestamp, lpt, offset, huh, recv_len);
527 // debug(1,"Clock: %" PRIx64 ", UT: %016" PRIx64 ", correctedPOT: %016" PRIx64 ", part of
528 // lastGMPhaseChange: %016" PRIx64 ".", packet_clock_id, correctedPOT - lpt, correctedPOT,
529 // lpt);
530
531 } else {
532 debug(1, "Follow_Up message is too small to be valid.");
533 }
534 }
535 }