return response;
}
+const char *get_client_name(int client_id) {
+ if ((client_id >= 0) && (client_id < MAX_CLIENTS)) {
+ return clients[client_id].shm_interface_name;
+ } else {
+ return "";
+ }
+}
+
int get_client_id(char *client_shared_memory_interface_name) {
int response = -1; // signify not found
if (client_shared_memory_interface_name != NULL) {
strncpy((char *)&clocks_private_info[i].ip, sender_string,
FIELD_SIZEOF(clock_source_private_data, ip) - 1);
clocks_private_info[i].family = family;
-#ifdef MAX_TIMING_SAMPLES
- clocks_private_info[i].vacant_samples = MAX_TIMING_SAMPLES;
-#endif
clocks_private_info[i].flags |= (1 << clock_is_in_use);
debug(2, "create record for ip: %s, family: %s.", &clocks_private_info[i].ip,
clocks_private_info[i].family == AF_INET6 ? "IPv6" : "IPv4");
// do a garbage collect for clock records no longer in use
for (i = 0; i < MAX_CLOCKS; i++) {
// only if its in use and not a timing peer... don't need a mutex to check
- if (((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) &&
- ((clocks_private_info[i].flags & (1 << clock_is_a_timing_peer)) == 0)) {
- int64_t time_since_last_use = reception_time - clocks_private_info[i].time_of_last_use;
- // using a sync timeout to determine when to drop the record...
- // the following give the sync receipt time in whole seconds
- // depending on the aPTPinitialLogSyncInterval and the aPTPsyncReceiptTimeout
- int64_t syncTimeout = (1 << (32 + aPTPinitialLogSyncInterval));
- syncTimeout = syncTimeout * aPTPsyncReceiptTimeout;
- syncTimeout = syncTimeout >> 32;
- // seconds to nanoseconds
- syncTimeout = syncTimeout * 1000000000;
- if (time_since_last_use > syncTimeout) {
- uint32_t old_flags = clocks_private_info[i].flags;
- debug(2, "delete record for: %s.", &clocks_private_info[i].ip);
- memset(&clocks_private_info[i], 0, sizeof(clock_source_private_data));
- if (old_flags != 0)
- update_master(0); // TODO
- else
- debug_log_nqptp_status(2);
+ // TODO -- check all clients to see if it's in use
+ if ((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) {
+ int clock_is_a_timing_peer_somewhere = 0;
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clocks_private_info[i].client_flags[temp_client_id] & (1 << clock_is_a_timing_peer)) !=
+ 0) {
+ clock_is_a_timing_peer_somewhere = 1;
+ }
+ }
+ if (clock_is_a_timing_peer_somewhere == 0) {
+ int64_t time_since_last_use = reception_time - clocks_private_info[i].time_of_last_use;
+ // using a sync timeout to determine when to drop the record...
+ // the following give the sync receipt time in whole seconds
+ // depending on the aPTPinitialLogSyncInterval and the aPTPsyncReceiptTimeout
+ int64_t syncTimeout = (1 << (32 + aPTPinitialLogSyncInterval));
+ syncTimeout = syncTimeout * aPTPsyncReceiptTimeout;
+ syncTimeout = syncTimeout >> 32;
+ // seconds to nanoseconds
+ syncTimeout = syncTimeout * 1000000000;
+ if (time_since_last_use > syncTimeout) {
+ uint32_t old_flags = clocks_private_info[i].flags;
+ debug(2, "delete record for: %s.", &clocks_private_info[i].ip);
+ memset(&clocks_private_info[i], 0, sizeof(clock_source_private_data));
+ if (old_flags != 0) {
+ update_master(0); // TODO -- won't be needed
+ } else {
+ debug_log_nqptp_status(2);
+ }
+ }
}
}
}
}
void debug_log_nqptp_status(int level) {
+/*
int records_in_use = 0;
int i;
for (i = 0; i < MAX_CLOCKS; i++)
} else {
debug(level, "Current NQPTP Status: no records in use.");
}
+*/
}
int uint32_cmp(uint32_t a, uint32_t b, const char *cause) {
}
}
-void update_master(int client_id) {
+void old_update_master() {
// This implements the IEEE 1588-2008 best master clock algorithm.
int best_so_far = -1;
int timing_peer_count = 0;
uint32_t acceptance_mask =
- (1 << clock_is_qualified) | (1 << clock_is_a_timing_peer) | (1 << clock_is_valid);
+// (1 << clock_is_qualified) | (1 << clock_is_a_timing_peer) | (1 << clock_is_valid);
+ (1 << clock_is_qualified) | (1 << clock_is_a_timing_peer);
for (i = 0; i < MAX_CLOCKS; i++) {
if ((clocks_private[i].flags & acceptance_mask) == acceptance_mask) {
// found a possible clock candidate
// if (old_master != -1) {
// but there was a master clock, so remove it
debug(1, "Remove master clock.");
- update_master_clock_info(0, NULL, 0, 0, 0);
+ update_master_clock_info(0, 0, NULL, 0, 0, 0);
//}
if (timing_peer_count == 0)
debug(2, "no valid qualified clocks ");
}
debug_log_nqptp_status(2);
}
+
+void update_master(int client_id) {
+ old_update_master(); // TODO -- for compatibility
+
+ // This implements the IEEE 1588-2008 best master clock algorithm.
+
+ // However, since nqptp is not a ptp clock, some of it doesn't apply.
+ // Specifically, the Identity of Receiver stuff doesn't apply, since the
+ // program is merely monitoring Announce message data and isn't a PTP clock itself
+ // and thus does not have any kind or receiver identity itself.
+
+ // Clock information coming from the same clock over IPv4 and IPv6 should have different
+ // port numbers.
+
+ // Figure 28 can be therefore be simplified considerably:
+
+ // Since nqptp can not be a receiver, and since nqptp can not originate a clock
+ // (and anyway nqptp filters out packets coming from self)
+ // we can do a single comparison of stepsRemoved and pick the shorter, if any.
+
+ // Figure 28 reduces to checking steps removed and then, if necessary, checking identities.
+ // If we see two identical sets of information, it is an error,
+ // but we leave things as they are.
+ int old_master = -1;
+ // find the current master clock if there is one and turn off all mastership
+ int i;
+ for (i = 0; i < MAX_CLOCKS; i++) {
+ if ((clocks_private[i].client_flags[client_id] & (1 << clock_is_master)) != 0)
+ if (old_master == -1)
+ old_master = i; // find old master
+ clocks_private[i].client_flags[client_id] &= ~(1 << clock_is_master); // turn them all off
+ clocks_private[i].client_flags[client_id] &=
+ ~(1 << clock_is_becoming_master); // turn them all off
+ }
+
+ int best_so_far = -1;
+ int timing_peer_count = 0;
+// uint32_t clock_specific_acceptance_mask = (1 << clock_is_qualified) | (1 << clock_is_valid);
+ uint32_t clock_specific_acceptance_mask = (1 << clock_is_qualified);
+ uint32_t client_specific_acceptance_mask = (1 << clock_is_a_timing_peer);
+ for (i = 0; i < MAX_CLOCKS; i++) {
+ if (((clocks_private[i].flags & clock_specific_acceptance_mask) ==
+ clock_specific_acceptance_mask) &&
+ ((clocks_private[i].client_flags[client_id] & client_specific_acceptance_mask) ==
+ client_specific_acceptance_mask)) {
+ // found a possible clock candidate
+ timing_peer_count++;
+ int outcome;
+ if (best_so_far == -1) {
+ best_so_far = i;
+ } else {
+ // Do the data set comparison detailed in Figure 27 and Figure 28 on pp89-90
+ if (clocks_private[i].grandmasterIdentity ==
+ clocks_private[best_so_far].grandmasterIdentity) {
+ // Do the relevant part of Figure 28:
+ outcome = uint32_cmp(clocks_private[i].stepsRemoved,
+ clocks_private[best_so_far].stepsRemoved, "steps removed");
+ // we need to check the portIdentify, which is the clock_id and the clock_port_number
+ if (outcome == 0)
+ outcome = uint64_cmp(clocks_private[i].clock_id, clocks_private[best_so_far].clock_id,
+ "clock id");
+ if (outcome == 0)
+ outcome =
+ uint32_cmp(clocks_private[i].clock_port_number,
+ clocks_private[best_so_far].clock_port_number, "clock port number");
+ if (outcome == 0) {
+ debug(1,
+ "Best Master Clock algorithm: two separate but identical potential clock "
+ "masters: %" PRIx64 ".",
+ clocks_private[best_so_far].clock_id);
+ }
+
+ } else {
+ outcome =
+ uint32_cmp(clocks_private[i].grandmasterPriority1,
+ clocks_private[best_so_far].grandmasterPriority1, "grandmasterPriority1");
+ if (outcome == 0)
+ outcome = uint32_cmp(clocks_private[i].grandmasterClass,
+ clocks_private[best_so_far].grandmasterClass, "grandmasterClass");
+ if (outcome == 0)
+ outcome =
+ uint32_cmp(clocks_private[i].grandmasterAccuracy,
+ clocks_private[best_so_far].grandmasterAccuracy, "grandmasterAccuracy");
+ if (outcome == 0)
+ outcome =
+ uint32_cmp(clocks_private[i].grandmasterVariance,
+ clocks_private[best_so_far].grandmasterVariance, "grandmasterVariance");
+ if (outcome == 0)
+ outcome = uint32_cmp(clocks_private[i].grandmasterPriority2,
+ clocks_private[best_so_far].grandmasterPriority2,
+ "grandmasterPriority2");
+ if (outcome == 0)
+ // this can't fail, as it's a condition of entering this section that they are different
+ outcome =
+ uint64_cmp(clocks_private[i].grandmasterIdentity,
+ clocks_private[best_so_far].grandmasterIdentity, "grandmasterIdentity");
+ }
+ if (outcome == -1)
+ best_so_far = i;
+ }
+ }
+ }
+ if (best_so_far == -1) {
+ // no master clock
+ // if (old_master != -1) {
+ // but there was a master clock, so remove it
+ debug(1, "Remove master clock.");
+ update_master_clock_info(client_id, 0, NULL, 0, 0, 0);
+ //}
+ if (timing_peer_count == 0)
+ debug(2, "no valid qualified clocks ");
+ else
+ debug(1, "no master clock!");
+ } else {
+ // we found a master clock
+
+ if (old_master != best_so_far) {
+ // if the master is a new one
+ // now, if it's already a master somewhere, it doesn't need to resync
+ int clock_is_a_master_somewhere = 0;
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clocks_private[best_so_far].client_flags[temp_client_id] & (1 << clock_is_master)) !=
+ 0) {
+ clock_is_a_master_somewhere = 1;
+ }
+ }
+ if (clock_is_a_master_somewhere == 0) {
+ clocks_private[best_so_far].client_flags[client_id] |= (1 << clock_is_becoming_master);
+ // clocks_private[best_so_far].flags |= (1 << clock_is_becoming_master); // to avoid searching
+ } else {
+ clocks_private[best_so_far].client_flags[client_id] |= (1 << clock_is_master);
+ // the smi will be updated when the next followup occurs
+ }
+ } else {
+ // if it's the same one as before
+ clocks_private[best_so_far].client_flags[client_id] |= (1 << clock_is_master);
+ }
+ }
+ debug_log_nqptp_status(2);
+}
+
+void update_master_clock_info(int client_id, uint64_t master_clock_id, const char *ip,
+ uint64_t local_time, uint64_t local_to_master_offset,
+ uint64_t mastership_start_time) {
+ // for compatibility
+ old_update_master_clock_info(master_clock_id, ip, local_time, local_to_master_offset,
+ mastership_start_time);
+ if (clients[client_id].shm_interface_name[0] != '\0') {
+ // debug(1,"update_master_clock_info start");
+ if (clients[client_id].shared_memory->master_clock_id != master_clock_id)
+ debug_log_nqptp_status(1);
+ int rc = pthread_mutex_lock(&clients[client_id].shared_memory->shm_mutex);
+ if (rc != 0)
+ warn("Can't acquire mutex to update master clock!");
+ clients[client_id].shared_memory->master_clock_id = master_clock_id;
+ if (ip != NULL) {
+ strncpy((char *)&clients[client_id].shared_memory->master_clock_ip, ip,
+ FIELD_SIZEOF(struct shm_structure, master_clock_ip) - 1);
+ clients[client_id].shared_memory->master_clock_start_time = mastership_start_time;
+ clients[client_id].shared_memory->local_time = local_time;
+ clients[client_id].shared_memory->local_to_master_time_offset = local_to_master_offset;
+ } else {
+ clients[client_id].shared_memory->master_clock_ip[0] = '\0';
+ clients[client_id].shared_memory->master_clock_start_time = 0;
+ clients[client_id].shared_memory->local_time = 0;
+ clients[client_id].shared_memory->local_to_master_time_offset = 0;
+ }
+ rc = pthread_mutex_unlock(&clients[client_id].shared_memory->shm_mutex);
+ if (rc != 0)
+ warn("Can't release mutex after updating master clock!");
+ // debug(1,"update_master_clock_info done");
+ }
+}
clock_private_info->flags &= ~(1 << clock_is_qualified);
else
clock_private_info->flags |= (1 << clock_is_qualified);
- update_master(0); // TODO -- use client_id here
+ // check/update the mastership of any clients that might be affected
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clock_private_info->client_flags[temp_client_id] &
+ (1 << clock_is_a_timing_peer)) != 0) {
+ debug(1, "clock_is_qualified -- updating clock mastership for client \"%s\"",
+ get_client_name(temp_client_id));
+ update_master(temp_client_id);
+ }
+ }
+ update_master(0); // TODO -- won't be needed
}
} else {
if ((clock_private_info->flags & (1 << clock_is_qualified)) !=
clock_private_info->announcements_without_followups = 0; // we've seen a followup
-#ifdef MAX_TIMING_SAMPLES
- clock_private_info->samples[clock_private_info->next_sample_goes_here].local_time =
- reception_time;
- clock_private_info->samples[clock_private_info->next_sample_goes_here].clock_time =
- preciseOriginTimestamp;
-
- if (clock_private_info->vacant_samples > 0)
- clock_private_info->vacant_samples--;
-
- clock_private_info->next_sample_goes_here++;
- // if we have need to wrap.
- if (clock_private_info->next_sample_goes_here == MAX_TIMING_SAMPLES)
- clock_private_info->next_sample_goes_here = 0;
-#endif
-
debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id, &clock_private_info->ip);
uint64_t offset = preciseOriginTimestamp - reception_time;
time_since_previous_offset = reception_time - clock_private_info->previous_offset_time;
}
- if ((clock_private_info->flags & (1 << clock_is_becoming_master)) != 0) {
- // we definitely have at least one sample since the request was made to
- // designate it a master, so we assume it is legitimate. That is, we assume
- // that the clock originator knows that it a clock master by now.
-#ifdef MAX_TIMING_SAMPLES
- uint64_t oldest_acceptable_master_clock_time =
- clock_private_info->source_time + 1150000000; // ns.
-
- // we will try to improve on this present, definitive, local_to_source_time_offset we have
- int changes_made = 0;
-
- uint64_t best_offset_so_far = clock_private_info->local_to_source_time_offset;
- uint64_t age_of_oldest_legitimate_sample = clock_private_info->local_time;
-
- int number_of_samples = MAX_TIMING_SAMPLES - clock_private_info->vacant_samples;
- int samples_checked = 0;
- if (number_of_samples > 0) {
- debug(3, "Number of samples: %d.", number_of_samples);
-
- // Now we use the last few samples to calculate the best offset for the
- // new master clock.
-
- // The time of the oldest sample we use will become the time of the start of the
- // mastership.
-
- // We will accept samples that would make the local-to-clock offset greatest,
- // provided they are not too old and that they don't push the current clock time
- // more than, say, 1000 ms plus one sample interval (i.e about 1.125 seconds) in the future.
-
- // This present sample is the only time estimate we have when the clock is definitely a
- // master, so we use it to eliminate any previous time estimates, made when the clock wasn't
- // designated a master, that would put it more than, say, a 1.15 seconds further into the
- // future.
-
- // Allow the samples to give a valid master clock time up to this much later than the
- // present, definitive, sample:
-
- uint64_t oldest_acceptable_time = reception_time - 10000000000; // only go back this far (ns)
-
- int64_t cko = age_of_oldest_legitimate_sample - oldest_acceptable_time;
- if (cko < 0)
- debug(1, "starting sample is too old: %" PRId64 " ns.", cko);
-
- int i;
- for (i = 0; i < number_of_samples; i++) {
- int64_t age = reception_time - clock_private_info->samples[i].local_time;
- int64_t age_relative_to_oldest_acceptable_time =
- clock_private_info->samples[i].local_time - oldest_acceptable_time;
- if (age_relative_to_oldest_acceptable_time > 0) {
- debug(3, "sample accepted at %f seconds old.", 0.000000001 * age);
- if (clock_private_info->samples[i].local_time < age_of_oldest_legitimate_sample) {
- age_of_oldest_legitimate_sample = clock_private_info->samples[i].local_time;
- }
- uint64_t possible_offset =
- clock_private_info->samples[i].clock_time - clock_private_info->samples[i].local_time;
- uint64_t possible_master_clock_time = clock_private_info->local_time + possible_offset;
- int64_t age_relative_to_oldest_acceptable_master_clock_time =
- possible_master_clock_time - oldest_acceptable_master_clock_time;
- if (age_relative_to_oldest_acceptable_master_clock_time <= 0) {
- samples_checked++;
- // so, the sample was not obtained too far in the past
- // and it would not push the estimated master clock_time too far into the future
- // so, if it is greater than the best_offset_so_far, then make it the new one
- if (possible_offset > best_offset_so_far) {
- debug(3, "new best offset");
- best_offset_so_far = possible_offset;
- changes_made++;
- }
- } else {
- debug(3, "sample too far into the future");
+ int clock_is_becoming_master_somewhere = 0;
+ {
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_becoming_master)) !=
+ 0) {
+ clock_is_becoming_master_somewhere = 1;
}
- } else {
- debug(3, "sample too old at %f seconds old.", 0.000000001 * age);
}
}
- }
- clock_private_info->mastership_start_time = age_of_oldest_legitimate_sample;
- int64_t offset_difference =
- best_offset_so_far - clock_private_info->local_to_source_time_offset;
- debug(2, "Lookback difference: %f ms with %d samples checked of %d samples total.",
- 0.000001 * offset_difference, samples_checked, number_of_samples);
- clock_private_info->local_to_source_time_offset = best_offset_so_far;
- debug(2, "Master sampling started %f ms before becoming master.",
- 0.000001 * (reception_time - age_of_oldest_legitimate_sample));
-#else
+ if ((clock_private_info->flags & (1 << clock_is_becoming_master)) != 0)
+ clock_is_becoming_master_somewhere = 1;
+
+ if (clock_is_becoming_master_somewhere != 0) {
+ // we now definitely have at least one sample since a request was made to
+ // designate this clock a master, so we assume it is legitimate. That is, we assume
+ // that the clock originator knows that it a clock master by now.
clock_private_info->mastership_start_time = clock_private_info->local_time;
-#endif
- clock_private_info->flags &= ~(1 << clock_is_becoming_master);
- clock_private_info->flags |= 1 << clock_is_master;
+
+ // designate the clock as master wherever is was becoming a master
+ {
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_becoming_master)) != 0) {
+ debug(1, "clock_is_becoming_master -- updating to clock_is_master for client \"%s\"",
+ get_client_name(temp_client_id));
+ clock_private_info->client_flags[temp_client_id] &= ~(1 << clock_is_becoming_master);
+ clock_private_info->client_flags[temp_client_id] |= (1 << clock_is_master);
+ }
+ }
+ }
+
+ clock_private_info->flags &= ~(1 << clock_is_becoming_master); // won't need this
+ clock_private_info->flags |= 1 << clock_is_master; // won't need this
clock_private_info->previous_offset_time = 0;
debug_log_nqptp_status(2);
} else if ((clock_private_info->previous_offset_time != 0) &&
offset = clock_private_info->previous_offset; // forget the present sample...
}
} else {
- if ((clock_private_info->flags & (1 << clock_is_master)) != 0)
+ int clock_is_a_master_somewhere = 0;
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_master)) !=
+ 0) {
+ clock_is_a_master_somewhere = 1;
+ }
+ }
+
+ if (clock_is_a_master_somewhere != 0)
debug(1, "Resynchronising master clock %" PRIx64 " at %s.", clock_private_info->clock_id,
clock_private_info->ip);
// leave the offset as it was coming in and take it as a sync time
clock_private_info->previous_offset = offset;
clock_private_info->previous_offset_time = reception_time;
+ int temp_client_id;
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_master)) != 0) {
+ debug(1, "clock_is_master -- updating master clock info for client \"%s\"",
+ get_client_name(temp_client_id));
+ update_master_clock_info(temp_client_id, clock_private_info->clock_id,
+ (const char *)&clock_private_info->ip, reception_time, offset,
+ clock_private_info->mastership_start_time);
+ }
+ }
+
+ // TODO -- remove the following when we are done
if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
- update_master_clock_info(clock_private_info->clock_id, (const char *)&clock_private_info->ip,
+ update_master_clock_info(0, clock_private_info->clock_id, (const char *)&clock_private_info->ip,
reception_time, offset, clock_private_info->mastership_start_time);
debug(3, "clock: %" PRIx64 ", time: %" PRIu64 ", offset: %" PRId64 ", jitter: %+f ms.",
clock_private_info->clock_id, reception_time, offset, 0.000001 * jitter);
}
-
+ /*
if ((clock_private_info->flags & (1 << clock_is_valid)) == 0) {
debug(2, "follow_up seen from %" PRIx64 " at %s.", clock_private_info->clock_id,
clock_private_info->ip);
clock_private_info->flags |=
(1 << clock_is_valid); // valid because it has at least one follow_up
- update_master(0); // TODO
+
+ for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
+ if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_a_timing_peer)) != 0) {
+ debug(1, "clock_is_valid has become true -- updating clock mastership for client \"%s\"",
+ get_client_name(temp_client_id));
+ update_master(temp_client_id);
+ }
+ }
+ update_master(0); // TODO -- won't be needed
}
+ */
}