]> git.ipfire.org Git - thirdparty/nqptp.git/commitdiff
* Bump NQPTP_SHM_STRUCTURES_VERSION to 8.
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 12 Sep 2022 14:07:15 +0000 (15:07 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 12 Sep 2022 14:07:15 +0000 (15:07 +0100)
* Remove the ability to handle multiple instances of AirPlay-2-capable Shairport Sync on the same system -- it seems clear that clients can not use this facility.
* Greatly simplify NQPTP by only monitoring PTP information coming from the client, ignoring all other PTP information.
* Improve the accuracy of the clock by taking correction field information into account.
* In addition to trying to restart a clock that is silent, also send a restart to a clock if the clock's grandmaster appears to have stopped.

Squashed commit of the following:
    Add some experimental exploratory code.
    Ignore the INSTALL document. Let INSTALL always be autogenerated.
    Enhanced and corrected ptp defiinitions.
    Fix shm name to work with FreeBSD. Update the SHM version.
    Change order of includes to avoid a compilation error in FreeBSD
    Fix potential bugs in finding a clock ID in a FreeBSD device.
    Only try to restart a stopped clock if it's the clock itself that has stopped and not a different grandmaster. Allow fast changes for up to one second after the start of mastership.

13 files changed:
.gitignore
INSTALL [deleted file]
general-utilities.c
general-utilities.h
nqptp-clock-sources.c
nqptp-clock-sources.h
nqptp-message-handlers.c
nqptp-message-handlers.h
nqptp-ptp-definitions.h
nqptp-shm-structures.h
nqptp-utilities.h
nqptp.c
nqptp.h

index f78c7e598d861dbc1a9ec279b86a895b986c6b07..b54d1dfe5116d8717b58b92932c923bfd5f7b030 100644 (file)
@@ -10,6 +10,7 @@ configure~
 
 # http://www.gnu.org/software/autoconf
 
+INSTALL
 autom4te.cache
 /autoscan.log
 /autoscan-*.log
diff --git a/INSTALL b/INSTALL
deleted file mode 100644 (file)
index e82fd21..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,368 +0,0 @@
-Installation Instructions
-*************************
-
-   Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free
-Software Foundation, Inc.
-
-   Copying and distribution of this file, with or without modification,
-are permitted in any medium without royalty provided the copyright
-notice and this notice are preserved.  This file is offered as-is,
-without warranty of any kind.
-
-Basic Installation
-==================
-
-   Briefly, the shell command './configure && make && make install'
-should configure, build, and install this package.  The following
-more-detailed instructions are generic; see the 'README' file for
-instructions specific to this package.  Some packages provide this
-'INSTALL' file but do not implement all of the features documented
-below.  The lack of an optional feature in a given package is not
-necessarily a bug.  More recommendations for GNU packages can be found
-in *note Makefile Conventions: (standards)Makefile Conventions.
-
-   The 'configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation.  It uses
-those values to create a 'Makefile' in each directory of the package.
-It may also create one or more '.h' files containing system-dependent
-definitions.  Finally, it creates a shell script 'config.status' that
-you can run in the future to recreate the current configuration, and a
-file 'config.log' containing compiler output (useful mainly for
-debugging 'configure').
-
-   It can also use an optional file (typically called 'config.cache' and
-enabled with '--cache-file=config.cache' or simply '-C') that saves the
-results of its tests to speed up reconfiguring.  Caching is disabled by
-default to prevent problems with accidental use of stale cache files.
-
-   If you need to do unusual things to compile the package, please try
-to figure out how 'configure' could check whether to do them, and mail
-diffs or instructions to the address given in the 'README' so they can
-be considered for the next release.  If you are using the cache, and at
-some point 'config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
-   The file 'configure.ac' (or 'configure.in') is used to create
-'configure' by a program called 'autoconf'.  You need 'configure.ac' if
-you want to change it or regenerate 'configure' using a newer version of
-'autoconf'.
-
-   The simplest way to compile this package is:
-
-  1. 'cd' to the directory containing the package's source code and type
-     './configure' to configure the package for your system.
-
-     Running 'configure' might take a while.  While running, it prints
-     some messages telling which features it is checking for.
-
-  2. Type 'make' to compile the package.
-
-  3. Optionally, type 'make check' to run any self-tests that come with
-     the package, generally using the just-built uninstalled binaries.
-
-  4. Type 'make install' to install the programs and any data files and
-     documentation.  When installing into a prefix owned by root, it is
-     recommended that the package be configured and built as a regular
-     user, and only the 'make install' phase executed with root
-     privileges.
-
-  5. Optionally, type 'make installcheck' to repeat any self-tests, but
-     this time using the binaries in their final installed location.
-     This target does not install anything.  Running this target as a
-     regular user, particularly if the prior 'make install' required
-     root privileges, verifies that the installation completed
-     correctly.
-
-  6. You can remove the program binaries and object files from the
-     source code directory by typing 'make clean'.  To also remove the
-     files that 'configure' created (so you can compile the package for
-     a different kind of computer), type 'make distclean'.  There is
-     also a 'make maintainer-clean' target, but that is intended mainly
-     for the package's developers.  If you use it, you may have to get
-     all sorts of other programs in order to regenerate files that came
-     with the distribution.
-
-  7. Often, you can also type 'make uninstall' to remove the installed
-     files again.  In practice, not all packages have tested that
-     uninstallation works correctly, even though it is required by the
-     GNU Coding Standards.
-
-  8. Some packages, particularly those that use Automake, provide 'make
-     distcheck', which can by used by developers to test that all other
-     targets like 'make install' and 'make uninstall' work correctly.
-     This target is generally not run by end users.
-
-Compilers and Options
-=====================
-
-   Some systems require unusual options for compilation or linking that
-the 'configure' script does not know about.  Run './configure --help'
-for details on some of the pertinent environment variables.
-
-   You can give 'configure' initial values for configuration parameters
-by setting variables in the command line or in the environment.  Here is
-an example:
-
-     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
-   *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
-   You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory.  To do this, you can use GNU 'make'.  'cd' to the
-directory where you want the object files and executables to go and run
-the 'configure' script.  'configure' automatically checks for the source
-code in the directory that 'configure' is in and in '..'.  This is known
-as a "VPATH" build.
-
-   With a non-GNU 'make', it is safer to compile the package for one
-architecture at a time in the source code directory.  After you have
-installed the package for one architecture, use 'make distclean' before
-reconfiguring for another architecture.
-
-   On MacOS X 10.5 and later systems, you can create libraries and
-executables that work on multiple system types--known as "fat" or
-"universal" binaries--by specifying multiple '-arch' options to the
-compiler but only a single '-arch' option to the preprocessor.  Like
-this:
-
-     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
-                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
-                 CPP="gcc -E" CXXCPP="g++ -E"
-
-   This is not guaranteed to produce working output in all cases, you
-may have to build one architecture at a time and combine the results
-using the 'lipo' tool if you have problems.
-
-Installation Names
-==================
-
-   By default, 'make install' installs the package's commands under
-'/usr/local/bin', include files under '/usr/local/include', etc.  You
-can specify an installation prefix other than '/usr/local' by giving
-'configure' the option '--prefix=PREFIX', where PREFIX must be an
-absolute file name.
-
-   You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files.  If you
-pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
-   In addition, if you use an unusual directory layout you can give
-options like '--bindir=DIR' to specify different values for particular
-kinds of files.  Run 'configure --help' for a list of the directories
-you can set and what kinds of files go in them.  In general, the default
-for these options is expressed in terms of '${prefix}', so that
-specifying just '--prefix' will affect all of the other directory
-specifications that were not explicitly provided.
-
-   The most portable way to affect installation locations is to pass the
-correct locations to 'configure'; however, many packages provide one or
-both of the following shortcuts of passing variable assignments to the
-'make install' command line to change installation locations without
-having to reconfigure or recompile.
-
-   The first method involves providing an override variable for each
-affected directory.  For example, 'make install
-prefix=/alternate/directory' will choose an alternate location for all
-directory configuration variables that were expressed in terms of
-'${prefix}'.  Any directories that were specified during 'configure',
-but not in terms of '${prefix}', must each be overridden at install time
-for the entire installation to be relocated.  The approach of makefile
-variable overrides for each directory variable is required by the GNU
-Coding Standards, and ideally causes no recompilation.  However, some
-platforms have known limitations with the semantics of shared libraries
-that end up requiring recompilation when using this method, particularly
-noticeable in packages that use GNU Libtool.
-
-   The second method involves providing the 'DESTDIR' variable.  For
-example, 'make install DESTDIR=/alternate/directory' will prepend
-'/alternate/directory' before all installation names.  The approach of
-'DESTDIR' overrides is not required by the GNU Coding Standards, and
-does not work on platforms that have drive letters.  On the other hand,
-it does better at avoiding recompilation issues, and works well even
-when some directory options were not specified in terms of '${prefix}'
-at 'configure' time.
-
-Optional Features
-=================
-
-   If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving 'configure' the
-option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
-
-   Some packages pay attention to '--enable-FEATURE' options to
-'configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to '--with-PACKAGE' options, where PACKAGE
-is something like 'gnu-as' or 'x' (for the X Window System).  The
-'README' should mention any '--enable-' and '--with-' options that the
-package recognizes.
-
-   For packages that use the X Window System, 'configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the 'configure' options '--x-includes=DIR' and
-'--x-libraries=DIR' to specify their locations.
-
-   Some packages offer the ability to configure how verbose the
-execution of 'make' will be.  For these packages, running './configure
---enable-silent-rules' sets the default to minimal output, which can be
-overridden with 'make V=1'; while running './configure
---disable-silent-rules' sets the default to verbose, which can be
-overridden with 'make V=0'.
-
-Particular systems
-==================
-
-   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU CC
-is not installed, it is recommended to use the following options in
-order to use an ANSI C compiler:
-
-     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
-
-and if that doesn't work, install pre-built binaries of GCC for HP-UX.
-
-   HP-UX 'make' updates targets which have the same timestamps as their
-prerequisites, which makes it generally unusable when shipped generated
-files such as 'configure' are involved.  Use GNU 'make' instead.
-
-   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
-parse its '<wchar.h>' header file.  The option '-nodtk' can be used as a
-workaround.  If GNU CC is not installed, it is therefore recommended to
-try
-
-     ./configure CC="cc"
-
-and if that doesn't work, try
-
-     ./configure CC="cc -nodtk"
-
-   On Solaris, don't put '/usr/ucb' early in your 'PATH'.  This
-directory contains several dysfunctional programs; working variants of
-these programs are available in '/usr/bin'.  So, if you need '/usr/ucb'
-in your 'PATH', put it _after_ '/usr/bin'.
-
-   On Haiku, software installed for all users goes in '/boot/common',
-not '/usr/local'.  It is recommended to use the following options:
-
-     ./configure --prefix=/boot/common
-
-Specifying the System Type
-==========================
-
-   There may be some features 'configure' cannot figure out
-automatically, but needs to determine by the type of machine the package
-will run on.  Usually, assuming the package is built to be run on the
-_same_ architectures, 'configure' can figure that out, but if it prints
-a message saying it cannot guess the machine type, give it the
-'--build=TYPE' option.  TYPE can either be a short name for the system
-type, such as 'sun4', or a canonical name which has the form:
-
-     CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
-     OS
-     KERNEL-OS
-
-   See the file 'config.sub' for the possible values of each field.  If
-'config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
-   If you are _building_ compiler tools for cross-compiling, you should
-use the option '--target=TYPE' to select the type of system they will
-produce code for.
-
-   If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with '--host=TYPE'.
-
-Sharing Defaults
-================
-
-   If you want to set default values for 'configure' scripts to share,
-you can create a site shell script called 'config.site' that gives
-default values for variables like 'CC', 'cache_file', and 'prefix'.
-'configure' looks for 'PREFIX/share/config.site' if it exists, then
-'PREFIX/etc/config.site' if it exists.  Or, you can set the
-'CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all 'configure' scripts look for a site script.
-
-Defining Variables
-==================
-
-   Variables not defined in a site shell script can be set in the
-environment passed to 'configure'.  However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost.  In order to avoid this problem, you should set
-them in the 'configure' command line, using 'VAR=value'.  For example:
-
-     ./configure CC=/usr/local2/bin/gcc
-
-causes the specified 'gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
-Autoconf limitation.  Until the limitation is lifted, you can use this
-workaround:
-
-     CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-'configure' Invocation
-======================
-
-   'configure' recognizes the following options to control how it
-operates.
-
-'--help'
-'-h'
-     Print a summary of all of the options to 'configure', and exit.
-
-'--help=short'
-'--help=recursive'
-     Print a summary of the options unique to this package's
-     'configure', and exit.  The 'short' variant lists options used only
-     in the top level, while the 'recursive' variant lists options also
-     present in any nested packages.
-
-'--version'
-'-V'
-     Print the version of Autoconf used to generate the 'configure'
-     script, and exit.
-
-'--cache-file=FILE'
-     Enable the cache: use and save the results of the tests in FILE,
-     traditionally 'config.cache'.  FILE defaults to '/dev/null' to
-     disable caching.
-
-'--config-cache'
-'-C'
-     Alias for '--cache-file=config.cache'.
-
-'--quiet'
-'--silent'
-'-q'
-     Do not print messages saying which checks are being made.  To
-     suppress all normal output, redirect it to '/dev/null' (any error
-     messages will still be shown).
-
-'--srcdir=DIR'
-     Look for the package's source code in directory DIR.  Usually
-     'configure' can determine that directory automatically.
-
-'--prefix=DIR'
-     Use DIR as the installation prefix.  *note Installation Names:: for
-     more details, including other options available for fine-tuning the
-     installation locations.
-
-'--no-create'
-'-n'
-     Run the configure checks, but stop before creating any output
-     files.
-
-'configure' also accepts some other, not widely useful, options.  Run
-'configure --help' for more details.
index cd9cff339c1347ff1cdf37262fab6576e31f4139..4d16043083471829183986a9c9e8df700f1e12cf 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
index 527b1ab4dba041a62668537ac69b485dde054eef..33d0caae29840be3874505b37ad30637a2b4a572 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
index f0a6ad4917f1ff4acf2f33639f6148d933a60798..616652b68cb8d0d9c80a51c1612e8c84e77e5d02 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -43,6 +43,9 @@
 #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];
 
@@ -241,240 +244,6 @@ int create_clock_source_record(char *sender_string,
   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
-    // 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
-          }
-        }
-      }
-    }
-  }
-}
-
-// 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].flags &= ~(1 << clock_is_one_of_ours);
-  }
-
-  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 clock_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].flags |= (1 << clock_is_one_of_ours);
-            }
-          }
-        }
-      }
-    }
-    freeifaddrs(ifap);
-  } else {
-    debug(1, "getifaddrs error - %s.", strerror(errno));
-  }
-}
-
-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(int client_id) {
-
-  // 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
-  }
-
-  int best_so_far = -1;
-  int timing_peer_count = 0;
-  uint32_t clock_specific_acceptance_mask = (1 << clock_is_announced);
-  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) || (best_so_far != old_master)) {
-  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 information from interface %s.", get_client_name(client_id));
-    update_master_clock_info(client_id, 0, NULL, 0, 0, 0);
-    //}
-  }
-  if (best_so_far == -1) {
-    if (timing_peer_count == 0)
-      debug(2, "empty timing peer group ");
-    else
-      debug(1, "no master clock!");
-  } else {
-    // we mark the master clock we found
-    clocks_private[best_so_far].client_flags[client_id] |= (1 << clock_is_master);
-  }
-}
-
 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) {
@@ -503,3 +272,30 @@ void update_master_clock_info(int client_id, uint64_t master_clock_id, const cha
     // debug(1,"update_master_clock_info done");
   }
 }
+
+void new_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) {
+  // debug(1,"update_master_clock_info clock: % " PRIx64 ", offset: %" PRIx64 ".",
+  // master_clock_id, local_to_master_offset);
+  int rc = pthread_mutex_lock(&shared_memory->shm_mutex);
+  if (rc != 0)
+    warn("Can't acquire mutex to update master clock!");
+  shared_memory->master_clock_id = master_clock_id;
+  if (ip != NULL) {
+    strncpy((char *)&shared_memory->master_clock_ip, ip,
+            FIELD_SIZEOF(struct shm_structure, master_clock_ip) - 1);
+    shared_memory->master_clock_start_time = mastership_start_time;
+    shared_memory->local_time = local_time;
+    shared_memory->local_to_master_time_offset = local_to_master_offset;
+  } else {
+    shared_memory->master_clock_ip[0] = '\0';
+    shared_memory->master_clock_start_time = 0;
+    shared_memory->local_time = 0;
+    shared_memory->local_to_master_time_offset = 0;
+  }
+  rc = pthread_mutex_unlock(&shared_memory->shm_mutex);
+  if (rc != 0)
+    warn("Can't release mutex after updating master clock!");
+  // debug(1,"update_master_clock_info done");
+}
index 7b806d14d3a86f0f12ea18b2e4f708858bd1b2b7..dbbe7c0a97fe5b07a78bcc2ae7bef4ec63b94fac 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
 #ifndef NQPTP_CLOCK_SOURCES_H
 #define NQPTP_CLOCK_SOURCES_H
 
+#include "nqptp-shm-structures.h"
 #include "nqptp.h"
 
 typedef enum {
   clock_is_in_use,
-  clock_is_one_of_ours,
-  clock_is_a_timing_peer,
-  clock_is_announced,
   clock_is_master
 } clock_flags;
 
@@ -71,6 +69,9 @@ typedef struct {
                  // interface
 } client_record;
 
+extern int shm_fd;
+extern struct shm_structure *shared_memory;
+
 int find_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info);
 
 int create_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info);
@@ -86,10 +87,12 @@ int delete_clients();
 
 extern clock_source_private_data clocks_private[MAX_CLOCKS];
 
-void update_master(int client_id);
-
 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);
 
+void new_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);
+
 #endif
index f874339283ddf7ed6ee3e0bb5ea782bbf0f44fb5..9420baf5c1e4c42c560e8376b84c01a4993a8bf5 100644 (file)
 #include <arpa/inet.h> // ntohl and ntohs
 #include <string.h>    //strsep
 
+#include <stdio.h> // snprintf
+
 #include "debug.h"
 #include "general-utilities.h"
 #include "nqptp-message-handlers.h"
 #include "nqptp-ptp-definitions.h"
 #include "nqptp-utilities.h"
 
+char hexcharbuffer[16384];
+
+char *hex_string(void *buf, size_t buf_len) {
+  char *tbuf = (char *)buf;
+  char *obfp = hexcharbuffer;
+  size_t obfc;
+  for (obfc = 0; obfc < buf_len; obfc++) {
+    snprintf(obfp, 3, "%02X", *tbuf);
+    obfp += 2;
+    tbuf = tbuf+1;
+  };
+  *obfp = 0;
+  return hexcharbuffer;
+}
+
 void handle_control_port_messages(char *buf, ssize_t recv_len,
                                   clock_source_private_data *clock_private_info) {
   if (recv_len != -1) {
@@ -38,11 +55,18 @@ void handle_control_port_messages(char *buf, ssize_t recv_len,
       int client_id = 0;
       if (ip_list != NULL)
         command = strsep(&ip_list, " ");
+      debug(2,"Clear timing peer group.");
+      // dirty experimental hack -- delete all the clocks
+      int gc;
+      for (gc = 0; gc < MAX_CLOCKS; gc++) {
+        memset(&clock_private_info[gc], 0, sizeof(clock_source_private_data));
+      }
       if ((command == NULL) || ((strcmp(command, "T") == 0) && (ip_list == NULL))) {
-        // clear all the flags, but only if the client exists
-        client_id = get_client_id(smi_name); // create the record if it doesn't exist
+        
+        // clear all the flags
+        int client_id = get_client_id(smi_name); // create the record if it doesn't exist
         if (client_id != -1) {
-          // turn off all is_timing_peer flags
+          /*
           int i;
           for (i = 0; i < MAX_CLOCKS; i++) {
             // e.g. (obsolete)
@@ -67,27 +91,29 @@ void handle_control_port_messages(char *buf, ssize_t recv_len,
               }
             }
             clock_private_info[i].client_flags[client_id] = 0;
+            
           }
+          */
           update_master_clock_info(client_id, 0, NULL, 0, 0, 0); // it may have obsolete stuff in it
         }
+        new_update_master_clock_info(0, NULL, 0, 0, 0); // it may have obsolete stuff in it
       } else {
         debug(2, "get or create new record for \"%s\".", smi_name);
         client_id = get_client_id(smi_name); // create the record if it doesn't exist
         if (client_id != -1) {
           if (strcmp(command, "T") == 0) {
-            // turn off all is_timing_peer flags
             int i;
             for (i = 0; i < MAX_CLOCKS; i++) {
-              clock_private_info[i].flags &=
-                  ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!)
-              clock_private_info[i].client_flags[client_id] &=
-                  ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!)
               clock_private_info[i].announcements_without_followups =
                   0; // to allow a possibly silent clock to be revisited when added to a timing
                      // peer list
               clock_private_info[i].follow_up_number = 0;
             }
-            while (ip_list != NULL) {
+
+            // take the first ip and make it the master, permanently
+            
+            
+            if (ip_list != NULL) {
               char *new_ip = strsep(&ip_list, " ");
               // look for the IP in the list of clocks, and create an inert entry if not there
               if ((new_ip != NULL) && (new_ip[0] != 0)) {
@@ -95,22 +121,30 @@ void handle_control_port_messages(char *buf, ssize_t recv_len,
                 if (t == -1)
                   t = create_clock_source_record(new_ip, clock_private_info);
                 if (t != -1) { // if the clock table is not full, show it's a timing peer
-                  clock_private_info[t].client_flags[client_id] |= (1 << clock_is_a_timing_peer);
+                  clock_private_info[t].client_flags[client_id] |= (1 << clock_is_master);
                 }
                 // otherwise, drop it
               }
+              
+              
             }
 
-            // now find and mark the best clock in the timing peer list as the master
-            update_master(client_id);
 
-            debug(2, "Timing group start");
-            for (i = 0; i < MAX_CLOCKS; i++) {
-              if ((clock_private_info[i].client_flags[client_id] & (1 << clock_is_a_timing_peer)) !=
-                  0)
-                debug(2, "%s.", &clock_private_info[i].ip);
+/*
+            while (ip_list != NULL) {
+              char *new_ip = strsep(&ip_list, " ");
+              // look for the IP in the list of clocks, and create an inert entry if not there
+              if ((new_ip != NULL) && (new_ip[0] != 0)) {
+                int t = find_clock_source_record(new_ip, clock_private_info);
+                if (t == -1)
+                  t = create_clock_source_record(new_ip, clock_private_info);
+                if (t != -1) { // if the clock table is not full, show it's a timing peer
+                  clock_private_info[t].client_flags[client_id] |= (1 << clock_is_a_timing_peer);
+                }
+                // otherwise, drop it
+              }
             }
-            debug(2, "Timing group end");
+*/
           } else {
             warn("Unrecognised string on the control port.");
           }
@@ -128,112 +162,49 @@ void handle_control_port_messages(char *buf, ssize_t recv_len,
 
 void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
                      __attribute__((unused)) uint64_t reception_time) {
-  // only process Announce messages that do not come from self
-  if ((clock_private_info->flags & (1 << clock_is_one_of_ours)) == 0) {
-    // debug_print_buffer(1, buf, (size_t) recv_len);
-    // make way for the new time
-    if ((size_t)recv_len >= sizeof(struct ptp_announce_message)) {
-      struct ptp_announce_message *msg = (struct ptp_announce_message *)buf;
-
-      uint64_t packet_clock_id = nctohl(&msg->header.clockIdentity[0]);
-      uint64_t packet_clock_id_low = nctohl(&msg->header.clockIdentity[4]);
-      packet_clock_id = packet_clock_id << 32;
-      packet_clock_id = packet_clock_id + packet_clock_id_low;
-      clock_private_info->flags |= (1 << clock_is_announced);
-      clock_private_info->clock_id = packet_clock_id;
-      clock_private_info->grandmasterPriority1 =
-          msg->announce.grandmasterPriority1; // need this for possibly pinging it later...
-      clock_private_info->grandmasterPriority2 =
-          msg->announce.grandmasterPriority2; // need this for possibly pinging it later...
-
-      debug(2, "announcement seen from %" PRIx64 " at %s.", clock_private_info->clock_id,
-            clock_private_info->ip);
-
-      if (clock_private_info->announcements_without_followups < 5) // don't keep going forever
-        // a value of 4 means it's parked --
-        // it has seen three, poked the clock and doesn't want to do any more.
-        clock_private_info->announcements_without_followups++;
-
-      uint64_t grandmaster_clock_id = nctohl(&msg->announce.grandmasterIdentity[0]);
-      uint64_t grandmaster_clock_id_low = nctohl(&msg->announce.grandmasterIdentity[4]);
-      grandmaster_clock_id = grandmaster_clock_id << 32;
-      grandmaster_clock_id = grandmaster_clock_id + grandmaster_clock_id_low;
-      uint32_t clockQuality = ntohl(msg->announce.grandmasterClockQuality);
-      uint8_t clockClass = (clockQuality >> 24) & 0xff;
-      uint8_t clockAccuracy = (clockQuality >> 16) & 0xff;
-      uint16_t offsetScaledLogVariance = clockQuality & 0xffff;
-      uint16_t stepsRemoved = ntohs(msg->announce.stepsRemoved);
-      uint16_t sourcePortID = ntohs(msg->header.sourcePortID);
-
-      // something in might have changed that
-      // affects its status as a possible master clock.
-      int best_clock_update_needed = 0;
-      if (clock_private_info->grandmasterIdentity != grandmaster_clock_id) {
-        clock_private_info->grandmasterIdentity = grandmaster_clock_id;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->grandmasterPriority1 != msg->announce.grandmasterPriority1) {
-        clock_private_info->grandmasterPriority1 = msg->announce.grandmasterPriority1;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->grandmasterQuality != clockQuality) {
-        clock_private_info->grandmasterQuality = clockQuality;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->grandmasterClass != clockClass) {
-        clock_private_info->grandmasterClass = clockClass;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->grandmasterAccuracy != clockAccuracy) {
-        clock_private_info->grandmasterAccuracy = clockAccuracy;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->grandmasterVariance != offsetScaledLogVariance) {
-        clock_private_info->grandmasterVariance = offsetScaledLogVariance;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->grandmasterPriority2 != msg->announce.grandmasterPriority2) {
-        clock_private_info->grandmasterPriority2 = msg->announce.grandmasterPriority2;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->stepsRemoved != stepsRemoved) {
-        clock_private_info->stepsRemoved = stepsRemoved;
-        best_clock_update_needed = 1;
-      }
-      if (clock_private_info->clock_port_number != sourcePortID) {
-        clock_private_info->clock_port_number = sourcePortID;
-        best_clock_update_needed = 1;
-      }
-
-      if (best_clock_update_needed) {
-        debug(2, "best clock update needed");
-        debug(2, "    grandmasterIdentity:         %" PRIx64 ".", grandmaster_clock_id);
-        debug(2, "    grandmasterPriority1:        %u.", msg->announce.grandmasterPriority1);
-        debug(2, "    grandmasterClockQuality:     0x%x.", clockQuality);
-        debug(2, "        clockClass:              %u.", clockClass); // See 7.6.2.4 clockClass
-        debug(2, "        clockAccuracy:           0x%x.",
-              clockAccuracy); // See 7.6.2.5 clockAccuracy
-        debug(2, "        offsetScaledLogVariance: 0x%x.",
-              offsetScaledLogVariance); // See 7.6.3 PTP variance
-        debug(2, "    grandmasterPriority2:        %u.", msg->announce.grandmasterPriority2);
-        debug(2, "    stepsRemoved:                %u.", stepsRemoved);
-        debug(2, "    portNumber:                  %u.", sourcePortID);
-
-        // 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(2,
-                  "best_clock_update_needed because %" PRIx64
-                  " on ip %s has changed -- updating clock mastership for client \"%s\"",
-                  clock_private_info->clock_id, clock_private_info->ip,
-                  get_client_name(temp_client_id));
-            update_master(temp_client_id);
-          }
-        }
-      }
-    }
+  // debug_print_buffer(1, buf, (size_t) recv_len);
+  // make way for the new time
+  if ((size_t)recv_len >= sizeof(struct ptp_announce_message)) {
+    struct ptp_announce_message *msg = (struct ptp_announce_message *)buf;
+
+    uint64_t packet_clock_id = nctohl(&msg->header.clockIdentity[0]);
+    uint64_t packet_clock_id_low = nctohl(&msg->header.clockIdentity[4]);
+    packet_clock_id = packet_clock_id << 32;
+    packet_clock_id = packet_clock_id + packet_clock_id_low;
+    clock_private_info->clock_id = packet_clock_id;
+    clock_private_info->grandmasterPriority1 =
+        msg->announce.grandmasterPriority1; // need this for possibly pinging it later...
+    clock_private_info->grandmasterPriority2 =
+        msg->announce.grandmasterPriority2; // need this for possibly pinging it later...
+
+    debug(2, "announcement seen from %" PRIx64 " at %s.", clock_private_info->clock_id,
+          clock_private_info->ip);
+
+    if (clock_private_info->announcements_without_followups < 5) // don't keep going forever
+      // a value of 4 means it's parked --
+      // it has seen three, poked the clock and doesn't want to do any more.
+      clock_private_info->announcements_without_followups++;
+
+    uint64_t grandmaster_clock_id = nctohl(&msg->announce.grandmasterIdentity[0]);
+    uint64_t grandmaster_clock_id_low = nctohl(&msg->announce.grandmasterIdentity[4]);
+    grandmaster_clock_id = grandmaster_clock_id << 32;
+    grandmaster_clock_id = grandmaster_clock_id + grandmaster_clock_id_low;
+    uint32_t clockQuality = ntohl(msg->announce.grandmasterClockQuality);
+    uint8_t clockClass = (clockQuality >> 24) & 0xff;
+    uint8_t clockAccuracy = (clockQuality >> 16) & 0xff;
+    uint16_t offsetScaledLogVariance = clockQuality & 0xffff;
+    uint16_t stepsRemoved = ntohs(msg->announce.stepsRemoved);
+    uint16_t sourcePortID = ntohs(msg->header.sourcePortID);
+
+    clock_private_info->grandmasterIdentity = grandmaster_clock_id;
+    clock_private_info->grandmasterPriority1 = msg->announce.grandmasterPriority1;
+    clock_private_info->grandmasterQuality = clockQuality;
+    clock_private_info->grandmasterClass = clockClass;
+    clock_private_info->grandmasterAccuracy = clockAccuracy;
+    clock_private_info->grandmasterVariance = offsetScaledLogVariance;
+    clock_private_info->grandmasterPriority2 = msg->announce.grandmasterPriority2;
+    clock_private_info->stepsRemoved = stepsRemoved;
+    clock_private_info->clock_port_number = sourcePortID;
   }
 }
 
@@ -243,15 +214,6 @@ void handle_sync(char *buf, ssize_t recv_len, clock_source_private_data *clock_p
     debug(2, "Sync received before announcement -- discarded.");
   } else {
     if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_sync_message))) {
-      int is_a_master = 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)
-          is_a_master = 1;
-
-      // only process it if it's a master somewhere...
-
-      if (is_a_master) {
         // debug_print_buffer(1, buf, recv_len);
         struct ptp_sync_message *msg = (struct ptp_sync_message *)buf;
 
@@ -275,7 +237,6 @@ void handle_sync(char *buf, ssize_t recv_len, clock_source_private_data *clock_p
           debug(1, "Sync correction field is non-zero: %" PRId64 " ns.", correction_field);
 
         correction_field = correction_field / 65536; // might be signed
-      }
     } else {
       debug(1, "Sync message is too small to be valid.");
     }
@@ -289,15 +250,6 @@ void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *cl
   } else {
     clock_private_info->announcements_without_followups = 0;
     if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_follow_up_message))) {
-      int is_a_master = 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)
-          is_a_master = 1;
-
-      // only process it if it's a master somewhere...
-
-      if (is_a_master) {
         // debug_print_buffer(1, buf, recv_len);
         struct ptp_follow_up_message *msg = (struct ptp_follow_up_message *)buf;
         uint16_t seconds_hi = nctohs(&msg->follow_up.preciseOriginTimestamp[0]);
@@ -325,8 +277,9 @@ void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *cl
                   clock_private_info->clock_id, 0.000000001 * duration_of_mastership);
             int64_t wait_limit = 62;
             wait_limit = wait_limit * 1000000000;
-            if (duration_of_mastership <= wait_limit) {
-              debug(2,
+            // only try to restart a grandmaster clock on the clock itself.
+            if ((duration_of_mastership <= wait_limit) && (clock_private_info->clock_id == clock_private_info->grandmasterIdentity)) {
+              debug(1,
                     "Attempt to start a stopped clock %" PRIx64
                     ", at follow_up_number %u at IP %s.",
                     clock_private_info->clock_id, clock_private_info->follow_up_number,
@@ -435,17 +388,19 @@ void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *cl
         // Follow-ups don't always come in at 125 ms intervals, especially after a discontinuity
         // Delays makes the offsets smaller than they should be, which is quickly
         // allowed for.
+        
+        int64_t mastership_time = reception_time - clock_private_info->mastership_start_time;
+        if (clock_private_info->mastership_start_time == 0)
+          mastership_time = 0;
 
         if ((clock_private_info->previous_offset_time != 0) && (jitter > -10000000)) {
 
           if (jitter < 0) {
-            if (clock_private_info->follow_up_number <
-                (5 * 8)) // at the beginning (8 samples per second)
+            if (mastership_time < 1000000000) // at the beginning
               smoothed_offset = clock_private_info->previous_offset + jitter / 16;
             else
               smoothed_offset = clock_private_info->previous_offset + jitter / 64;
-          } else if (clock_private_info->follow_up_number <
-                     (5 * 8)) // at the beginning (8 samples per second)
+          } else if (mastership_time < 1000000000) // at the beginning
             smoothed_offset =
                 clock_private_info->previous_offset + jitter / 1; // accept positive changes quickly
           else
@@ -480,24 +435,57 @@ void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *cl
         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(2,
-                  "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
-                  ", smoothed offset: %" PRIx64 ". Raw Precise Origin Timestamp: %" PRIx64
-                  ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
-                  "%u. Source: %s",
-                  clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
-                  smoothed_offset, preciseOriginTimestamp, 0.000001 * time_since_previous_offset,
-                  ntohs(msg->header.sequenceId), clock_private_info->follow_up_number,
-                  clock_private_info->ip);
-
-            debug(2, "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,
-                                     smoothed_offset, clock_private_info->mastership_start_time);
+            // recalculate mastership_time because it might have been zeroed.
+            mastership_time = reception_time - clock_private_info->mastership_start_time;
+            if (clock_private_info->mastership_start_time == 0)
+              mastership_time = 0;
+            if (mastership_time > 200000000) {
+              int64_t delta = smoothed_offset - offset;
+              debug(2,
+                    "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
+                    ", smoothed offset: %" PRIx64 ". Smoothed Offset - Offset: %10.3f. Raw Precise Origin Timestamp: %" PRIx64
+                    ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
+                    "%u. Source: %s",
+                    clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
+                    smoothed_offset, 0.000001 * delta, preciseOriginTimestamp, 0.000001 * time_since_previous_offset,
+                    ntohs(msg->header.sequenceId), clock_private_info->follow_up_number,
+                    clock_private_info->ip);
+
+              debug(2, "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->grandmasterIdentity,
+                                       (const char *)&clock_private_info->ip, reception_time,
+                                       smoothed_offset, clock_private_info->mastership_start_time);
+            }
           }
         }
-      }
+
+        int64_t delta = smoothed_offset - offset;
+        debug(2,
+              "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
+              ", smoothed offset: %" PRIx64 ". Smoothed Offset - Offset: %10.3f. Raw Precise Origin Timestamp: %" PRIx64
+              ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
+              "%u. Source: %s",
+              clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
+              smoothed_offset, 0.000001 * delta, preciseOriginTimestamp, 0.000001 * time_since_previous_offset,
+              ntohs(msg->header.sequenceId), clock_private_info->follow_up_number,
+              clock_private_info->ip);
+
+        new_update_master_clock_info(clock_private_info->grandmasterIdentity,
+                         (const char *)&clock_private_info->ip, reception_time,
+                         smoothed_offset, clock_private_info->mastership_start_time);
+        
+        // now do some quick calculations on the possible "Universal Time"
+        // debug_print_buffer(1, "", buf, recv_len);
+        uint8_t *tlv = (uint8_t *)&msg->follow_up.tlvs[0];
+        uint8_t *lastGmPhaseChange = tlv + 16;
+        uint64_t lpt = nctoh64(lastGmPhaseChange + 4);
+        uint64_t last_tlv_clock = nctoh64((uint8_t *)buf + 86);
+        uint64_t huh = offset - lpt;
+        debug_print_buffer(2, buf, (size_t) recv_len);
+        debug(2, "%" PRIx64 ", %" PRIx64 ", %s, Origin: %016" PRIx64 ", LPT: %016" PRIx64 ", Offset: %016" PRIx64 ", Universal Offset: %016" PRIx64 ", packet length: %u.", clock_private_info->clock_id, last_tlv_clock,  hex_string(lastGmPhaseChange,12), preciseOriginTimestamp, lpt, offset, huh, recv_len);
+        // debug(1,"Clock: %" PRIx64 ", UT: %016" PRIx64 ", correctedPOT: %016" PRIx64 ", part of lastGMPhaseChange: %016" PRIx64 ".", packet_clock_id, correctedPOT - lpt, correctedPOT, lpt);
+
     } else {
       debug(1, "Follow_Up message is too small to be valid.");
     }
index 6d2e3428e7f30b2e5298ad071f06ae3cbed83d9d..72068c7ac8e431819180e750e98b43c185c9dd12 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
index 5250a560aea17971b7b1de43030e1b06fa145aec..fbe143061a3c693a76e3fe86c46780a4d3ff8747 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -98,12 +98,48 @@ enum messageType {
 // Table 34, part of
 enum tlvTypeValue {
   Reserved,
+  // Standard TLVs
   MANAGEMENT,
   MANAGEMENT_ERROR_STATUS,
-  ORGANIZATION_EXTENSION
+  ORGANIZATION_EXTENSION,
+  // Optional unicast message negotiation TLVs
+  REQUEST_UNICAST_TRANSMISSION,
+  GRANT_UNICAST_TRANSMISSION,
+  CANCEL_UNICAST_TRANSMISSION,
+  ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION,
+  // Optional path trace mechanism TLV
+  PATH_TRACE,
+  // Optional alternate timescale TLV
+  ALTERNATE_TIME_OFFSET_INDICATOR
   // there are more, but not needed yet
 };
 
+// Table 23
+enum controlFieldValue {
+  Control_Field_Value_Sync,
+  Control_Field_Value_Delay_Req,
+  Control_Field_Value_Follow_Up,
+  Control_Field_Value_Delay_Resp,
+  Control_Field_Value_Management,
+  Control_Field_Value_Other
+};
+
+// this is the structure of a PATH_TRACE TLV (16.2, Table 78, pp 164) without any space for the data
+struct __attribute__((__packed__)) ptp_path_trace_tlv {
+  uint16_t tlvType;
+  uint16_t lengthField;
+  uint8_t pathSequence[0];
+};
+
+// this is the structure of a TLV (14.3, Table 35, pp 135) without any space for the data
+struct __attribute__((__packed__)) ptp_tlv {
+  uint16_t tlvType;
+  uint16_t lengthField;
+  uint8_t organizationId[3];
+  uint8_t organizationSubType[3];
+  uint8_t dataField[0];
+};
+
 // this is the Common Message Header
 struct __attribute__((__packed__)) ptp_common_message_header {
   uint8_t transportSpecificAndMessageID; // 0x11
@@ -117,7 +153,7 @@ struct __attribute__((__packed__)) ptp_common_message_header {
   uint8_t clockIdentity[8];    // MAC
   uint16_t sourcePortID;       // 1
   uint16_t sequenceId;         // increments
-  uint8_t controlOtherMessage; // 5
+  uint8_t controlField; // 5
   uint8_t logMessagePeriod;    // 0
 };
 
@@ -132,6 +168,7 @@ struct __attribute__((__packed__)) ptp_announce {
   uint8_t grandmasterIdentity[8];
   uint16_t stepsRemoved;
   uint8_t timeSource;
+  struct ptp_path_trace_tlv path_trace[0];
 };
 
 // this is the extra part for a Sync or Delay_Req message
@@ -147,6 +184,8 @@ struct __attribute__((__packed__)) ptp_delay_req {
 // this is the extra part for a Follow_Up message
 struct __attribute__((__packed__)) ptp_follow_up {
   uint8_t preciseOriginTimestamp[10];
+  // to be followed by zero or more TLVs
+  struct ptp_tlv tlvs[0];
 };
 
 // this is the extra part for a Delay_Resp message
@@ -167,15 +206,6 @@ struct __attribute__((__packed__)) ptp_pdelay_resp {
   uint8_t requestingPortIdentity[10];
 };
 
-// this is the structure of a TLV (14.3, Table 35, pp 135) without any space for the data
-struct __attribute__((__packed__)) ptp_tlv {
-  uint16_t tlvType;
-  uint16_t lengthField;
-  uint8_t organizationId[3];
-  uint8_t organizationSubType[3];
-  uint8_t dataField[0];
-};
-
 // this is the extra part for a Signaling message (13.12, pp 132) without any TLVs
 struct __attribute__((__packed__)) ptp_signaling {
   uint8_t targetPortIdentity[10];
index f3861b44a7b8fddd57a06c066d4cb70a7fc33ac3..3dbe0c907bcb1273abe3c95856fb552200586d15 100644 (file)
@@ -20,7 +20,9 @@
 #ifndef NQPTP_SHM_STRUCTURES_H
 #define NQPTP_SHM_STRUCTURES_H
 
-#define NQPTP_SHM_STRUCTURES_VERSION 7
+#define NQPTP_INTERFACE_NAME "/nqptp"
+
+#define NQPTP_SHM_STRUCTURES_VERSION 8
 #define NQPTP_CONTROL_PORT 9000
 
 // The control port expects a UDP packet with the first space-delimited string
index 3d2e933dd92a95e07bd016384bf6a1e97283831c..82adad6d8dfd8b68fed94cb3e02ba4781580f135 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
diff --git a/nqptp.c b/nqptp.c
index e6f3d6cb7a2d7a9803b97e1e36ce3dbb4a838c50..4f8e50b97263bf003b97eab3aad4338894adb07d 100644 (file)
--- a/nqptp.c
+++ b/nqptp.c
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -89,9 +89,23 @@ void goodbye(void) {
   unsigned int i;
   for (i = 0; i < sockets_open_stuff.sockets_open; i++)
     close(sockets_open_stuff.sockets[i].number);
-  // close off shared memory interfaces
+    
 
+  // close off shared memory interface
   delete_clients();
+  
+  // close off new smi
+  // mmap cleanup
+  if (munmap(shared_memory, sizeof(struct shm_structure)) != 0) {
+    debug(1, "error unmapping shared memory");
+  }
+  // shm_open cleanup
+  if (shm_unlink(NQPTP_INTERFACE_NAME) == -1) {
+    debug(1, "error unlinking shared memory \"%s\"", NQPTP_INTERFACE_NAME);
+  }
+  
+  if (shm_fd != -1)
+    close(shm_fd);
 
   if (epoll_fd != -1)
     close(epoll_fd);
@@ -145,7 +159,7 @@ int main(int argc, char **argv) {
       }
     }
   }
-
+  
   debug_init(debug_level, 0, 1, 1);
   debug(1, "Startup. Clock ID: \"%" PRIx64 "\".", get_self_clock_id());
   // debug(1, "size of a clock entry is %u bytes.", sizeof(clock_source_private_data));
@@ -166,6 +180,65 @@ int main(int argc, char **argv) {
   memset(&act2, 0, sizeof(struct sigaction));
   act2.sa_handler = termHandler;
   sigaction(SIGTERM, &act2, NULL);
+  
+  // open the SMI
+
+  pthread_mutexattr_t shared;
+  int err;
+
+  shm_fd = -1;
+
+  mode_t oldumask = umask(0);
+  shm_fd = shm_open(NQPTP_INTERFACE_NAME, O_RDWR | O_CREAT, 0666);
+  if (shm_fd == -1) {
+    die("cannot open shared memory \"%s\".", NQPTP_INTERFACE_NAME);
+  }
+  (void)umask(oldumask);
+
+  if (ftruncate(shm_fd, sizeof(struct shm_structure)) == -1) {
+    die("failed to set size of shared memory \"%s\".", NQPTP_INTERFACE_NAME);
+  }
+
+#ifdef CONFIG_FOR_FREEBSD
+  shared_memory =
+      (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE,
+                                   MAP_SHARED, shm_fd, 0);
+#endif
+
+#ifdef CONFIG_FOR_LINUX
+  shared_memory =
+      (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE,
+                                   MAP_LOCKED | MAP_SHARED, shm_fd, 0);
+#endif
+
+  if (shared_memory == (struct shm_structure *)-1) {
+    die("failed to mmap shared memory \"%s\".", NQPTP_INTERFACE_NAME);
+  }
+
+  if (shm_fd == -1) {
+    warn("error closing \"%s\" after mapping.", shm_fd);
+  }
+
+  // zero it
+  memset(shared_memory, 0, sizeof(struct shm_structure));
+  shared_memory->version = NQPTP_SHM_STRUCTURES_VERSION;
+
+  /*create mutex attr */
+  err = pthread_mutexattr_init(&shared);
+  if (err != 0) {
+    die("mutex attribute initialization failed - %s.", strerror(errno));
+  }
+  pthread_mutexattr_setpshared(&shared, 1);
+  /*create a mutex */
+  err = pthread_mutex_init((pthread_mutex_t *)&shared_memory->shm_mutex, &shared);
+  if (err != 0) {
+    die("mutex initialization failed - %s.", strerror(errno));
+  }
+
+  err = pthread_mutexattr_destroy(&shared);
+  if (err != 0) {
+    die("mutex attribute destruction failed - %s.", strerror(errno));
+  }
 
   ssize_t recv_len;
 
@@ -288,22 +361,22 @@ int main(int argc, char **argv) {
                 char sender_string[256];
                 memset(sender_string, 0, sizeof(sender_string));
                 inet_ntop(connection_ip_family, sender_addr, sender_string, sizeof(sender_string));
-                // now, find or create a record for this ip
+                // now, find the record for this ip
                 int the_clock = find_clock_source_record(
                     sender_string, (clock_source_private_data *)&clocks_private);
                 // not sure about requiring a Sync before creating it...
                 // if ((the_clock == -1) && ((buf[0] & 0xF) == Sync)) {
+                /*
                 if (the_clock == -1) {
                   the_clock = create_clock_source_record(
                       sender_string, (clock_source_private_data *)&clocks_private);
                 }
+                */
                 if (the_clock != -1) {
                   clocks_private[the_clock].time_of_last_use =
                       reception_time; // for garbage collection
                   switch (buf[0] & 0xF) {
                   case Announce:
-                    // needed to reject messages coming from self
-                    update_clock_self_identifications((clock_source_private_data *)&clocks_private);
                     handle_announce(buf, recv_len, &clocks_private[the_clock], reception_time);
                     break;
                   case Follow_Up:
@@ -323,8 +396,8 @@ int main(int argc, char **argv) {
           }
         }
       }
-      if (retval >= 0)
-        manage_clock_sources(reception_time, (clock_source_private_data *)&clocks_private);
+      //if (retval >= 0)
+      //  manage_clock_sources(reception_time, (clock_source_private_data *)&clocks_private);
       int i;
       for (i = 0; i < TIMED_TASKS; i++) {
         if (timed_tasks[i].trigger_time != 0) {
@@ -356,7 +429,7 @@ void send_awakening_announcement_sequence(const uint64_t clock_id, const char *c
   msg->header.flags = htons(0x0408);
   hcton64(my_clock_id, &msg->header.clockIdentity[0]);
   msg->header.sourcePortID = htons(32776);
-  msg->header.controlOtherMessage = 0x05;
+  msg->header.controlField = 0x05;
   msg->header.logMessagePeriod = 0xFE;
   msg->announce.currentUtcOffset = htons(37);
   hcton64(my_clock_id, &msg->announce.grandmasterIdentity[0]);
@@ -446,10 +519,8 @@ uint64_t broadcasting_task(uint64_t call_time, __attribute__((unused)) void *pri
         is_a_master = 1;
 
     // only process it if it's a master somewhere...
-    if ((is_a_master != 0) && (clocks_private[i].announcements_without_followups == 3) &&
-        // (clocks_private[i].follow_up_number == 0) && // only check at the start
-        ((clocks_private[i].flags & (1 << clock_is_one_of_ours)) == 0)) {
-      debug(2,
+    if ((is_a_master != 0) && (clocks_private[i].announcements_without_followups == 3)) {
+      debug(1,
             "Attempt to awaken a silent clock %" PRIx64
             ", index %u, at follow_up_number %u at IP %s.",
             clocks_private[i].clock_id, i, clocks_private[i].follow_up_number,
diff --git a/nqptp.h b/nqptp.h
index 1070b9115db42090e45ee52c770d7e86bc8416b0..f26516fd25ee8b9cf57b4f7a4c0db0328bbdefcd 100644 (file)
--- a/nqptp.h
+++ b/nqptp.h
@@ -1,6 +1,6 @@
 /*
  * 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