/*
* This file is part of the nqptp distribution (https://github.com/mikebrady/nqptp).
- * Copyright (c) 2021 Mike Brady.
+ * Copyright (c) 2021-2022 Mike Brady.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
-#include <sys/types.h>
+#include <sys/types.h> // for ftruncate and others
+#include <unistd.h> // for ftruncate and others
+
+#include <fcntl.h> /* For O_* constants */
+#include <sys/mman.h> // for shared memory stuff
+#include <sys/select.h> // for fd_set
+#include <sys/stat.h> // umask
#ifdef CONFIG_FOR_FREEBSD
#include <netinet/in.h>
#define FIELD_SIZEOF(t, f) (sizeof(((t *)0)->f))
#endif
+int shm_fd;
+struct shm_structure *shared_memory;
+
clock_source_private_data clocks_private[MAX_CLOCKS];
+client_record clients[MAX_CLIENTS];
+
+/*
+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) {
+ int i = 0;
+ // first, see if yu can find it anywhere
+ while ((response == -1) && (i < MAX_CLIENTS)) {
+ if (strcmp(clients[i].shm_interface_name, client_shared_memory_interface_name) == 0)
+ response = i;
+ else
+ i++;
+ }
+
+ if (response == -1) { // no match, so create one
+ i = 0;
+ while ((response == -1) && (i < MAX_CLIENTS)) {
+ if (clients[i].shm_interface_name[0] == '\0')
+ response = i;
+ else
+ i++;
+ }
+ if (response != -1) {
+ strncpy(clients[i].shm_interface_name, client_shared_memory_interface_name,
+ sizeof(clients[i].shm_interface_name));
+ // create the named smi interface
+
+ // open a shared memory interface.
+ debug(2, "Create a shm interface named \"%s\"", clients[i].shm_interface_name);
+ clients[i].shm_fd = -1;
+
+ mode_t oldumask = umask(0);
+ clients[i].shm_fd = shm_open(client_shared_memory_interface_name, O_RDWR | O_CREAT, 0666);
+ if (clients[i].shm_fd == -1) {
+ die("cannot open shared memory \"%s\".", client_shared_memory_interface_name);
+ }
+ (void)umask(oldumask);
+
+ if (ftruncate(clients[i].shm_fd, sizeof(struct shm_structure)) == -1) {
+ die("failed to set size of shared memory \"%s\".", client_shared_memory_interface_name);
+ }
+
+#ifdef CONFIG_FOR_FREEBSD
+ clients[i].shared_memory =
+ (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE,
+ MAP_SHARED, clients[i].shm_fd, 0);
+#endif
+
+#ifdef CONFIG_FOR_LINUX
+ clients[i].shared_memory =
+ (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE,
+ MAP_LOCKED | MAP_SHARED, clients[i].shm_fd, 0);
+#endif
+
+ if (clients[i].shared_memory == (struct shm_structure *)-1) {
+ die("failed to mmap shared memory \"%s\".", client_shared_memory_interface_name);
+ }
+
+ if ((close(clients[i].shm_fd) == -1)) {
+ warn("error closing \"%s\" after mapping.", client_shared_memory_interface_name);
+ }
+
+ // zero it
+ memset(clients[i].shared_memory, 0, sizeof(struct shm_structure));
+ clients[i].shared_memory->version = NQPTP_SHM_STRUCTURES_VERSION;
+
+ // for (i = 0; i < MAX_CLOCKS; i++) {
+ // clocks_private[i].client_flags[response] =
+ // 0; // turn off all client flags in every clock for this client
+ // }
+ } else {
+ debug(1, "could not create a client record for client \"%s\".",
+ client_shared_memory_interface_name);
+ }
+ }
+ } else {
+ debug(1, "no client_shared_memory_interface_name");
+ }
+ debug(2, "get_client_id \"%s\" response %d", client_shared_memory_interface_name, response);
+ return response;
+}
+
+int delete_client(int client_id) {
+ int response = 0; // okay unless something happens
+ if (clients[client_id].shm_interface_name[0] != '\0') {
+ if (clients[client_id].shared_memory != NULL) {
+ // mmap cleanup
+ if (munmap(clients[client_id].shared_memory, sizeof(struct shm_structure)) != 0) {
+ debug(1, "error unmapping shared memory");
+ response = -1;
+ }
+ // shm_open cleanup
+ if (shm_unlink(clients[client_id].shm_interface_name) == -1) {
+ debug(1, "error unlinking shared memory \"%s\"", clients[client_id].shm_interface_name);
+ response = -1;
+ }
+ }
+ clients[client_id].shm_interface_name[0] = '\0'; // remove the name to signify it's vacant
+ }
+ return response;
+}
+
+int delete_clients() {
+ int response = 0; // okay unless something happens
+ int i;
+ for (i = 0; i < MAX_CLIENTS; i++)
+ if (delete_client(i) != 0)
+ response = -1;
+ return response;
+}
int find_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info) {
// return the index of the clock in the clock information arrays or -1
int i = 0;
int found = 0;
while ((found == 0) && (i < MAX_CLOCKS)) {
- if ((clocks_private_info[i].in_use != 0) &&
+ if (((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) &&
(strcasecmp(sender_string, (const char *)&clocks_private_info[i].ip) == 0))
found = 1;
else
int create_clock_source_record(char *sender_string,
clock_source_private_data *clocks_private_info) {
- // return the index of a clock entry in the clock information arrays or -1 if full
+ // return the index of a clock entry in the clock information arrays or -1 if full
// initialise the entries in the shared and private arrays
int response = -1;
int i = 0;
int found = 0; // trying to find an unused entry
while ((found == 0) && (i < MAX_CLOCKS)) {
- if (clocks_private_info[i].in_use == 0)
+ if ((clocks_private_info[i].flags & (1 << clock_is_in_use)) == 0)
found = 1;
else
i++;
strncpy((char *)&clocks_private_info[i].ip, sender_string,
FIELD_SIZEOF(clock_source_private_data, ip) - 1);
clocks_private_info[i].family = family;
- clocks_private_info[i].vacant_samples = MAX_TIMING_SAMPLES;
- clocks_private_info[i].in_use = 1;
+ 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");
} else {
return response;
}
-void manage_clock_sources(uint64_t reception_time, clock_source_private_data *clocks_private_info) {
- debug(3, "manage_clock_sources");
- int i;
-
- // 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].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();
- else
- debug_log_nqptp_status(2);
- }
- }
- }
-}
-
-// check all the entries in the clock array and mark all those that
-// belong to ourselves
-
-void update_clock_self_identifications(clock_source_private_data *clocks_private_info) {
- // first, turn off all the self-id flags
- int i;
- for (i = 0; i < MAX_CLOCKS; i++) {
- clocks_private_info[i].is_one_of_ours = 0;
- }
-
- struct ifaddrs *ifap, *ifa;
- void *addr = NULL;
- short family;
- int response = getifaddrs(&ifap);
- if (response == 0) {
- for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- struct sockaddr *my_ifa_addr = ifa->ifa_addr;
- if (my_ifa_addr) {
- family = my_ifa_addr->sa_family;
-#ifdef AF_INET6
- if (family == AF_INET6) {
- struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)my_ifa_addr;
- addr = &(sa6->sin6_addr);
- }
-#endif
- if (family == AF_INET) {
- struct sockaddr_in *sa4 = (struct sockaddr_in *)my_ifa_addr;
- addr = &(sa4->sin_addr);
- }
- char ip_string[64];
- memset(ip_string, 0, sizeof(ip_string));
- if (addr != NULL)
- inet_ntop(family, addr, ip_string, sizeof(ip_string));
- if (strlen(ip_string) != 0) {
- // now set the is_one_of_ours flag of any clock with this ip
- for (i = 0; i < MAX_CLOCKS; i++) {
- if (strcasecmp(ip_string, clocks_private_info[i].ip) == 0) {
- debug(2, "found an entry for one of our clocks");
- clocks_private_info[i].is_one_of_ours = 1;
- }
- }
- }
- } else {
- debug(1, "NULL ifa->ifa_addr. Probably harmless.");
- }
- }
- freeifaddrs(ifap);
- } else {
- debug(1, "getifaddrs error - %s.", strerror(errno));
- }
-}
-
-void debug_log_nqptp_status(int level) {
- int records_in_use = 0;
- int i;
- for (i = 0; i < MAX_CLOCKS; i++)
- if (clocks_private[i].in_use != 0)
- records_in_use++;
- debug(level, "");
- if (records_in_use > 0) {
- debug(level, "Current NQPTP Status:");
- uint32_t peer_mask = (1 << clock_is_a_timing_peer);
- uint32_t peer_clock_mask = peer_mask | (1 << clock_is_valid);
- uint32_t peer_master_mask = peer_clock_mask | (1 << clock_is_master);
- uint32_t peer_becoming_master_mask = peer_clock_mask | (1 << clock_is_becoming_master);
- uint32_t non_peer_clock_mask = (1 << clock_is_valid);
- uint32_t non_peer_master_mask = non_peer_clock_mask | (1 << clock_is_master);
- for (i = 0; i < MAX_CLOCKS; i++) {
- if (clocks_private[i].in_use != 0) {
- if ((clocks_private[i].flags & peer_master_mask) == peer_master_mask) {
- debug(level, " Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id,
- clocks_private[i].ip);
- } else if ((clocks_private[i].flags & peer_becoming_master_mask) ==
- peer_becoming_master_mask) {
- debug(level, " Peer Becoming Master: %" PRIx64 " %s.", clocks_private[i].clock_id,
- clocks_private[i].ip);
- } else if ((clocks_private[i].flags & peer_clock_mask) == peer_clock_mask) {
- debug(level, " Peer Clock: %" PRIx64 " %s.", clocks_private[i].clock_id,
- clocks_private[i].ip);
- } else if ((clocks_private[i].flags & peer_mask) == peer_mask) {
- debug(level, " Peer: %s.", clocks_private[i].ip);
- } else if ((clocks_private[i].flags & non_peer_master_mask) == non_peer_master_mask) {
- debug(level, " Non Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id,
- clocks_private[i].ip);
- } else if ((clocks_private[i].flags & non_peer_clock_mask) == non_peer_clock_mask) {
- debug(level, " Non Peer Clock: %16" PRIx64 " %s.", clocks_private[i].clock_id,
- clocks_private[i].ip);
- } else {
- debug(level, " Non Peer Record: %s.", clocks_private[i].ip);
- }
- }
- }
- } else {
- debug(level, "Current NQPTP Status: no records in use.");
- }
-}
-
-int uint32_cmp(uint32_t a, uint32_t b, const char *cause) {
- // returns -1 if a is less than b, 0 if a = b, +1 if a is greater than b
- if (a == b) {
- return 0;
- } else {
- debug(2, "Best Master Clock algorithm deciding factor: %s. Values: %u, %u.", cause, a, b);
- if (a < b)
- return -1;
- else
- return 1;
- }
-}
-
-int uint64_cmp(uint64_t a, uint64_t b, const char *cause) {
- // returns -1 if a is less than b, 0 if a = b, +1 if a is greater than b
- if (a == b) {
- return 0;
- } else {
- debug(2, "Best Master Clock algorithm deciding factor: %s. Values: %" PRIx64 ", %" PRIx64 ".",
- cause, a, b);
- if (a < b)
- return -1;
- else
- return 1;
- }
-}
-
-void update_master() {
-
- // 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].flags & (1 << clock_is_master)) != 0)
- if (old_master == -1)
- old_master = i; // find old master
- clocks_private[i].flags &= ~(1 << clock_is_master); // turn them all off
- clocks_private[i].flags &= ~(1 << clock_is_becoming_master); // turn them all off
- }
-
- 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);
- for (i = 0; i < MAX_CLOCKS; i++) {
- if ((clocks_private[i].flags & acceptance_mask) == 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(2, "Remove master clock.");
- update_master_clock_info(0, NULL, 0, 0, 0);
- }
- if (timing_peer_count == 0)
- debug(2, "no valid qualified clocks ");
- else
- debug(1, "no master clock!");
+void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time,
+ uint64_t local_to_master_offset, uint64_t mastership_start_time) {
+ // to ensure that a full update has taken place, the
+ // reader must ensure that the main and secondary
+ // structures are identical
+
+ shared_memory->main.master_clock_id = master_clock_id;
+ if (ip != NULL) {
+ shared_memory->main.master_clock_start_time = mastership_start_time;
+ shared_memory->main.local_time = local_time;
+ shared_memory->main.local_to_master_time_offset = local_to_master_offset;
} else {
- // we found a master clock
-
- if (old_master != best_so_far) {
- // if the master is a new one
- clocks_private[best_so_far].flags |= (1 << clock_is_becoming_master);
- } else {
- // if it's the same one as before
- clocks_private[best_so_far].flags |= (1 << clock_is_master);
- }
+ shared_memory->main.master_clock_start_time = 0;
+ shared_memory->main.local_time = 0;
+ shared_memory->main.local_to_master_time_offset = 0;
}
- debug_log_nqptp_status(2);
+ __sync_synchronize();
+ shared_memory->secondary = shared_memory->main;
+ __sync_synchronize();
}