]> git.ipfire.org Git - thirdparty/nqptp.git/commitdiff
Squashed commit of the following:
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 12 Sep 2022 14:14:52 +0000 (15:14 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 12 Sep 2022 14:14:52 +0000 (15:14 +0100)
commit 1c610279b21ac778ab96fd2fb01fa0bbc8f363ce
Author: Mike Brady <4265913+mikebrady@users.noreply.github.com>
Date:   Mon Sep 12 15:07:15 2022 +0100

    * Bump NQPTP_SHM_STRUCTURES_VERSION to 8.
    * 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 definitions.
        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.
        Make the code to restart a clock a separate function and call it if a clock gets stuck. Include the correction field in the calculation of the precise origin timestamp. If a new clock is out by more than -4 seconds, treat it as a discontinuity rather than noise.
        Add a net-to-host converter for 64-bit arithmetic

15 files changed:
.gitignore
INSTALL [deleted file]
RELEASE_NOTES.md
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.c
nqptp-utilities.h
nqptp.c
nqptp.h

index 16036191a2eed9d3d07d0b6c1270f958f5611082..b54d1dfe5116d8717b58b92932c923bfd5f7b030 100644 (file)
@@ -1,6 +1,7 @@
 # http://www.gnu.org/software/automake
 
 Makefile.in
+configure~
 /ar-lib
 /mdate-sh
 /py-compile
@@ -9,6 +10,7 @@ Makefile.in
 
 # 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 8865734..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,368 +0,0 @@
-Installation Instructions
-*************************
-
-   Copyright (C) 1994-1996, 1999-2002, 2004-2016 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 time stamps 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 1de97a8d9995cd5e5c1fa613101d1fd978a8bfc6..8aa7830a80f5b6513a93e009c657f6e2be32911e 100644 (file)
@@ -1,3 +1,7 @@
+## Version: 1.1-dev-186-g4e54f1b
+**Bug Fixes**
+* Reorder system header files includes to fix a compilation error.
+
 ## Version: 1.1-dev-166-g46a9f1b
 * Update the wording in the INSTALL document to match the wording generated at the `autoreconf -fi` stage, so that `git` doesn't flag an altered document. Thanks to [David Leibovic](https://github.com/dasl-) for bringing this to notice.
 
@@ -5,6 +9,12 @@
 **Bug Fix**
 * If a player (e.g. a HomePod mini) that was providing the master clock was removed from the set of devices playing, the new master clock retained out-of-date information about the old master clock. This could cause problems going to the next track or to a previous one, causing them not to be heard. Thanks (again!) to [Kristian Dimitrov](https://github.com/Kristian8606) for a precise description of how to cause the problem.
 
+## Version: 1.1-dev-164-g086a123
+**Enhancements**
+* Improve the accuracy of the clock by including data from the `correctionField` part of a PTP message. Most of the time, this is a fraction of a millisecond, but sometimes it can be larger.
+* If a clock timing sample is more than four seconds slow, treat it as the start of a new timing sequence rather than as an error in the current timing sequence.
+* Try to restart a clock that stops incrementing towards the start of a timing sequence.
+
 ## Version: 1.1-dev-154-g608980e
 **Bug Fix**
 * Some times, if a PTP clock went to sleep and then woke up, NQPTP would not recognise the new timing data, and, literally, get stuck in the past (!). Getting the problem to manifest itself was difficult -- the clock in the source device (e.g. an iPhone) had to sleep and restart at just the wrong time to cause the problem. Thanks to [Kristian Dimitrov](https://github.com/Kristian8606), [vision4u2](https://github.com/vision4u2) and others.
index b758c773e93f0d0f0d18fb64e5ec43d8f1d079e7..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
@@ -57,6 +57,19 @@ uint16_t nctohs(const uint8_t *p) { // read 2 characters from *p and do ntohs on
   return ntohs(holder);
 }
 
+uint64_t ntoh64(const uint64_t n) {
+  uint64_t fiddle = n;
+  uint32_t fiddle_hi = fiddle & 0xFFFFFFFF;
+  fiddle_hi = ntohl(fiddle_hi);
+  fiddle = fiddle >> 32;
+  uint32_t fiddle_lo = fiddle & 0xFFFFFFFF;
+  fiddle_lo = ntohl(fiddle_lo);
+  fiddle = fiddle_hi;
+  fiddle = fiddle << 32;
+  fiddle = fiddle | fiddle_lo;
+  return fiddle;
+}
+
 uint64_t timespec_to_ns(struct timespec *tn) {
   uint64_t tnfpsec = tn->tv_sec;
   uint64_t tnfpnsec = tn->tv_nsec;
@@ -70,6 +83,6 @@ uint64_t get_time_now() {
   clock_gettime(CLOCK_MONOTONIC_RAW, &tn);
 #else
   clock_gettime(CLOCK_MONOTONIC, &tn);
-#endif  
+#endif
   return timespec_to_ns(&tn);
 }
index 3dbeb89fdc19e34d263cf002f3624cbc908ffea3..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
@@ -45,4 +45,6 @@ uint16_t nctohs(const uint8_t *p);
 uint64_t timespec_to_ns(struct timespec *tn);
 uint64_t get_time_now();
 
+uint64_t ntoh64(const uint64_t n);
+
 #endif
\ No newline at end of file
index e9aa99828e3fce19f192c919175895a698534831..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,242 +244,12 @@ 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) {
-    // 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 (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) {
   if (clients[client_id].shm_interface_name[0] != '\0') {
-    // debug(1,"update_master_clock_info clock: % " PRIx64 ", offset: %" PRIx64 ".", master_clock_id, local_to_master_offset);
+    // debug(1,"update_master_clock_info clock: % " PRIx64 ", offset: %" PRIx64 ".",
+    // master_clock_id, local_to_master_offset);
     int rc = pthread_mutex_lock(&clients[client_id].shared_memory->shm_mutex);
     if (rc != 0)
       warn("Can't acquire mutex to update master clock!");
@@ -499,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 46740faad39d11950e7082b57f46e6c7bd454ce1..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;
 
@@ -37,7 +35,8 @@ typedef struct {
   int follow_up_number;
   int announcements_without_followups; // add 1 for every announce, reset with a followup
   uint64_t clock_id;
-  uint64_t previous_offset, previous_offset_time, previous_offset_grandmaster;
+  uint64_t previous_offset, previous_offset_time, previous_offset_grandmaster,
+      previous_preciseOriginTimestamp;
   uint64_t mastership_start_time; // set to the time of the first sample used as master
 
   // for garbage collection
@@ -57,6 +56,7 @@ typedef struct {
   uint8_t grandmasterPriority2;
   uint64_t grandmasterIdentity;
   uint16_t stepsRemoved;
+  int identical_previous_preciseOriginTimestamp_count;
 
 } clock_source_private_data;
 
@@ -69,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);
@@ -84,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 a5e6514d187a3627dd9bde526c77ae6573cc0f48..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,26 +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)) {
@@ -94,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.");
           }
@@ -127,251 +162,332 @@ 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;
   }
 }
 
-void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
-                      clock_source_private_data *clock_private_info, uint64_t reception_time) {
-
-  struct ptp_follow_up_message *msg = (struct ptp_follow_up_message *)buf;
-
-  uint16_t seconds_hi = nctohs(&msg->follow_up.preciseOriginTimestamp[0]);
-  uint32_t seconds_low = nctohl(&msg->follow_up.preciseOriginTimestamp[2]);
-  uint32_t nanoseconds = nctohl(&msg->follow_up.preciseOriginTimestamp[6]);
-  uint64_t preciseOriginTimestamp = seconds_hi;
-  preciseOriginTimestamp = preciseOriginTimestamp << 32;
-  preciseOriginTimestamp = preciseOriginTimestamp + seconds_low;
-  preciseOriginTimestamp = preciseOriginTimestamp * 1000000000L;
-  preciseOriginTimestamp = preciseOriginTimestamp + nanoseconds;
-  
-  // update our sample information
-
-  if (clock_private_info->follow_up_number < 100)
-    clock_private_info->follow_up_number++;
-
-  if (clock_private_info->announcements_without_followups < 4) // if we haven't signalled already
-    clock_private_info->announcements_without_followups = 0;   // we've seen a followup
-
-  debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id, &clock_private_info->ip);
-  uint64_t offset = preciseOriginTimestamp - reception_time;
-
-  int64_t jitter = 0;
-
-  int64_t time_since_previous_offset = 0;
-  uint64_t smoothed_offset = offset;
-  
-  // This is a bit hacky.
-  // Basically, the idea is that if the grandmaster has changed, then acceptance checking and smoothing
-  // should start as it it's a new clock. This is because the preciseOriginTimestamp, which is part of
-  // the data that is being smoothed, refers to the grandmaster, so when the grandmaster changes
-  // any previous calculations are no longer valid.
-  // The hacky bit is to signal this condition by zeroing the previous_offset_time.
-  if (clock_private_info->previous_offset_grandmaster != clock_private_info->grandmasterIdentity)
-    clock_private_info->previous_offset_time = 0; // the preciseOriginTimestamp always (?) refers to the grandmaster
-
-  if (clock_private_info->previous_offset_time != 0) {
-    time_since_previous_offset = reception_time - clock_private_info->previous_offset_time;
-  }
-
-  // Do acceptance checking and smoothing.
-
-  // Positive changes in the offset are much more likely to be
-  // legitimate, since they could only occur due to a shorter
-  // propagation time or less of a delay sending or receiving the packet.
-  // (Actually, this is not quite true --
-  // it is possible that the remote clock could be adjusted forward
-  // and this would increase the offset too.)
-  // Anyway, when the clock is new, we give extra preferential weighting to
-  // positive changes in the offset.
-
-  // If the new offset is greater, by any amount, than the old offset,
-  // or if it is less by up to 10 mS, accept it.
-  // Otherwise, drop it if the last sample was fairly recent
-  // If the last sample was long ago, take this as a discontinuity and
-  // accept it as the start of a new period of mastership.
-
-  // This seems to be quite stable
-
-  if (clock_private_info->previous_offset_time != 0)
-    jitter = offset - clock_private_info->previous_offset;
-
-  // We take any positive or a limited negative jitter as a sync event in
-  // a continuous synchronisation sequence.
-  // This works well with PTP sources that sleep, as when they sleep
-  // their clock stops. When they awaken, the offset from
-  // the local clock to them must be smaller than before, triggering the
-  // timing discontinuity below and allowing an immediate readjustment.
-
-  // The full value of a positive offset jitter is accepted for a
-  // number of follow_ups at the start.
-  // After that, the weight of the jitter is reduced.
-  // 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.
-
-  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)
-        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)
-      smoothed_offset =
-          clock_private_info->previous_offset + jitter / 1; // accept positive changes quickly
-    else
-      smoothed_offset = clock_private_info->previous_offset + jitter / 64;
+void handle_sync(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
+                 __attribute__((unused)) uint64_t reception_time) {
+  if (clock_private_info->clock_id == 0) {
+    debug(2, "Sync received before announcement -- discarded.");
   } else {
-    // allow samples to disappear for up to a second
-    if ((time_since_previous_offset != 0) && (time_since_previous_offset < 1000000000)) {
-      smoothed_offset =
-          clock_private_info
-              ->previous_offset + 1; // if we have recent samples, forget the present sample...
+    if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_sync_message))) {
+        // debug_print_buffer(1, buf, recv_len);
+        struct ptp_sync_message *msg = (struct ptp_sync_message *)buf;
+
+        // clang-format off
+      
+      // actually the precision timestamp needs to be corrected by the Follow_Up Correction_Field contents.
+      // According to IEEE Std 802.1AS-2020, paragraph 11.4.4.2.1:
+      /*
+      The value of the preciseOriginTimestamp field is the sourceTime of the ClockMaster entity of the Grandmaster PTP Instance,
+      when the associated Sync message was sent by that Grandmaster PTP Instance, with any fractional nanoseconds truncated (see 10.2.9).
+      The sum of the correctionFields in the Follow_Up and associated Sync messages, added to the preciseOriginTimestamp field of the Follow_Up message,
+      is the value of the synchronized time corresponding to the syncEventEgressTimestamp at the PTP Instance that sent the associated Sync message,
+      including any fractional nanoseconds.
+      */
+
+        // clang-format on
+
+        int64_t correction_field = ntoh64(msg->header.correctionField);
+
+        if (correction_field != 0)
+          debug(1, "Sync correction field is non-zero: %" PRId64 " ns.", correction_field);
+
+        correction_field = correction_field / 65536; // might be signed
     } else {
-      if (clock_private_info->previous_offset_time == 0)
-        debug(2, "Clock %" PRIx64 " record (re)starting at %s.", clock_private_info->clock_id,
-              clock_private_info->ip);
-      else
-        debug(2,
-              "Timing discontinuity on clock %" PRIx64
-              " at %s: time_since_previous_offset: %.3f seconds.",
-              clock_private_info->clock_id, clock_private_info->ip,
-              0.000000001 * time_since_previous_offset);
-      smoothed_offset = offset;
-      clock_private_info->follow_up_number = 0;
-      clock_private_info->mastership_start_time =
-          reception_time; // mastership is reset to this time...
+      debug(1, "Sync message is too small to be valid.");
     }
   }
+}
 
-  clock_private_info->previous_offset_grandmaster = clock_private_info->grandmasterIdentity;
-  clock_private_info->previous_offset = smoothed_offset;
-  clock_private_info->previous_offset_time = reception_time;
-    
-/*
-  debug(1,
-      "Clock %" PRIx64 ", grandmaster %" PRIx64 " at %s. Offset: %" PRIx64 ", smoothed offset: %" PRIx64
-      ". Precise Origin Timestamp: %" PRIx64
-      ". Time since previous offset: %.3f milliseconds.",
-      clock_private_info->clock_id, clock_private_info->grandmasterIdentity, clock_private_info->ip, offset, smoothed_offset,
-      preciseOriginTimestamp, 0.000001 * time_since_previous_offset);
-*/
+void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
+                      uint64_t reception_time) {
+  if (clock_private_info->clock_id == 0) {
+    debug(2, "Follow_Up received before announcement -- discarded.");
+  } else {
+    clock_private_info->announcements_without_followups = 0;
+    if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_follow_up_message))) {
+        // 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]);
+        uint32_t seconds_low = nctohl(&msg->follow_up.preciseOriginTimestamp[2]);
+        uint32_t nanoseconds = nctohl(&msg->follow_up.preciseOriginTimestamp[6]);
+        uint64_t preciseOriginTimestamp = seconds_hi;
+        preciseOriginTimestamp = preciseOriginTimestamp << 32;
+        preciseOriginTimestamp = preciseOriginTimestamp + seconds_low;
+        preciseOriginTimestamp = preciseOriginTimestamp * 1000000000L;
+        preciseOriginTimestamp = preciseOriginTimestamp + nanoseconds;
+
+        // update our sample information
+
+        if (clock_private_info->previous_preciseOriginTimestamp == preciseOriginTimestamp) {
+          clock_private_info->identical_previous_preciseOriginTimestamp_count++;
+
+          if (clock_private_info->identical_previous_preciseOriginTimestamp_count == 8 * 60) {
+            int64_t duration_of_mastership =
+                reception_time - clock_private_info->mastership_start_time;
+            if (clock_private_info->mastership_start_time == 0)
+              duration_of_mastership = 0;
+            debug(2,
+                  "Clock %" PRIx64
+                  "'s grandmaster clock has stopped after %f seconds of mastership.",
+                  clock_private_info->clock_id, 0.000000001 * duration_of_mastership);
+            int64_t wait_limit = 62;
+            wait_limit = wait_limit * 1000000000;
+            // 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,
+                    clock_private_info->ip);
+              send_awakening_announcement_sequence(
+                  clock_private_info->clock_id, clock_private_info->ip, clock_private_info->family,
+                  clock_private_info->grandmasterPriority1,
+                  clock_private_info->grandmasterPriority2);
+            }
+          }
+        } else {
+          if (clock_private_info->identical_previous_preciseOriginTimestamp_count >= 8 * 60) {
+            debug(2, "Clock %" PRIx64 "'s grandmaster clock has started again...",
+                  clock_private_info->clock_id);
+            clock_private_info->identical_previous_preciseOriginTimestamp_count = 0;
+          }
+        }
+
+        clock_private_info->previous_preciseOriginTimestamp = preciseOriginTimestamp;
+
+        // clang-format off
+      
+      // actually the precision timestamp needs to be corrected by the Follow_Up Correction_Field contents.
+      // According to IEEE Std 802.1AS-2020, paragraph 11.4.4.2.1:
+      /*
+      The value of the preciseOriginTimestamp field is the sourceTime of the ClockMaster entity of the Grandmaster PTP Instance,
+      when the associated Sync message was sent by that Grandmaster PTP Instance, with any fractional nanoseconds truncated (see 10.2.9).
+      The sum of the correctionFields in the Follow_Up and associated Sync messages, added to the preciseOriginTimestamp field of the Follow_Up message,
+      is the value of the synchronized time corresponding to the syncEventEgressTimestamp at the PTP Instance that sent the associated Sync message,
+      including any fractional nanoseconds.
+      */
+
+        // clang-format on
+
+        int64_t correction_field = ntoh64(msg->header.correctionField);
+
+        // debug(1," Check ntoh64: in: %" PRIx64 ", out: %" PRIx64 ".", msg->header.correctionField,
+        // correction_field);
+
+        correction_field = correction_field / 65536; // might be signed
+        uint64_t correctedPreciseOriginTimestamp = preciseOriginTimestamp + correction_field;
+
+        if (clock_private_info->follow_up_number < 100)
+          clock_private_info->follow_up_number++;
+
+        // if (clock_private_info->announcements_without_followups < 4) // if we haven't signalled
+        // already
+        clock_private_info->announcements_without_followups = 0; // we've seen a followup
+
+        debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id,
+              &clock_private_info->ip);
+        uint64_t offset = correctedPreciseOriginTimestamp - reception_time;
+
+        int64_t jitter = 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) {
-      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);
+        int64_t time_since_previous_offset = 0;
+        uint64_t smoothed_offset = offset;
+
+        // This is a bit hacky.
+        // Basically, the idea is that if the grandmaster has changed, then acceptance checking and
+        // smoothing should start as it it's a new clock. This is because the
+        // correctedPreciseOriginTimestamp, which is part of the data that is being smoothed, refers
+        // to the grandmaster, so when the grandmaster changes any previous calculations are no
+        // longer valid. The hacky bit is to signal this condition by zeroing the
+        // previous_offset_time.
+        if (clock_private_info->previous_offset_grandmaster !=
+            clock_private_info->grandmasterIdentity)
+          clock_private_info->previous_offset_time = 0;
+
+        if (clock_private_info->previous_offset_time != 0) {
+          time_since_previous_offset = reception_time - clock_private_info->previous_offset_time;
+        }
+
+        // Do acceptance checking and smoothing.
+
+        // Positive changes in the offset are much more likely to be
+        // legitimate, since they could only occur due to a shorter
+        // propagation time or less of a delay sending or receiving the packet.
+        // (Actually, this is not quite true --
+        // it is possible that the remote clock could be adjusted forward
+        // and this would increase the offset too.)
+        // Anyway, when the clock is new, we give extra preferential weighting to
+        // positive changes in the offset.
+
+        // If the new offset is greater, by any amount, than the old offset,
+        // or if it is less by up to 10 mS, accept it.
+        // Otherwise, drop it if the last sample was fairly recent
+        // If the last sample was long ago, take this as a discontinuity and
+        // accept it as the start of a new period of mastership.
+
+        // This seems to be quite stable
+
+        if (clock_private_info->previous_offset_time != 0)
+          jitter = offset - clock_private_info->previous_offset;
+
+        // We take any positive or a limited negative jitter as a sync event in
+        // a continuous synchronisation sequence.
+        // This works well with PTP sources that sleep, as when they sleep
+        // their clock stops. When they awaken, the offset from
+        // the local clock to them must be smaller than before, triggering the
+        // timing discontinuity below and allowing an immediate readjustment.
+
+        // The full value of a positive offset jitter is accepted for a
+        // number of follow_ups at the start.
+        // After that, the weight of the jitter is reduced.
+        // 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 (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 (mastership_time < 1000000000) // at the beginning
+            smoothed_offset =
+                clock_private_info->previous_offset + jitter / 1; // accept positive changes quickly
+          else
+            smoothed_offset = clock_private_info->previous_offset + jitter / 64;
+        } else {
+          // allow samples to disappear for up to a second
+          if ((time_since_previous_offset != 0) && (time_since_previous_offset < 1000000000) &&
+              (jitter > -4000000000L)) {
+            smoothed_offset = clock_private_info->previous_offset +
+                              1; // if we have recent samples, forget the present sample...
+          } else {
+            if (clock_private_info->previous_offset_time == 0)
+              debug(2, "Clock %" PRIx64 " record (re)starting at %s.", clock_private_info->clock_id,
+                    clock_private_info->ip);
+            else
+              debug(2,
+                    "Timing discontinuity on clock %" PRIx64
+                    " at %s: time_since_previous_offset: %.3f seconds.",
+                    clock_private_info->clock_id, clock_private_info->ip,
+                    0.000000001 * time_since_previous_offset);
+            smoothed_offset = offset;
+            // clock_private_info->follow_up_number = 0;
+            clock_private_info->mastership_start_time =
+                reception_time; // mastership is reset to this time...
+          }
+        }
+
+        clock_private_info->previous_offset_grandmaster = clock_private_info->grandmasterIdentity;
+        clock_private_info->previous_offset = smoothed_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) {
+            // 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 c89b359bc51e284f55471d4c43b02b6ceab74de1..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
@@ -28,14 +28,11 @@ void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clo
                      uint64_t reception_time);
 
 void handle_sync(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
-                 uint64_t reception_time, SOCKADDR *from_sock_addr, int socket_number);
+                 uint64_t reception_time);
 
 void handle_follow_up(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
                       uint64_t reception_time);
 
-void handle_delay_resp(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
-                       uint64_t reception_time);
-
 void handle_control_port_messages(char *buf, ssize_t recv_len,
                                   clock_source_private_data *clock_private_info);
 
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 0791197d0cc6a22df76ade5b4c29f497225113c0..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
@@ -41,7 +43,6 @@
 // (In future version of NQPTP the SMI interface may also be deleted at this point.)
 // SMI interfaces are not currently deleted or garbage collected.
 
-
 #include <inttypes.h>
 #include <pthread.h>
 
index d371aa296898a57e6492ecf6e9ee90c042c6b0d8..c3a5c7a623f28eb2bdbec4789d71f8f3e06b91fd 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
 #endif
 
 #ifdef CONFIG_FOR_FREEBSD
+#include <unistd.h>
 #include <sys/types.h>
-#include <net/if_types.h>
 #include <net/if_dl.h>
+#include <net/if_types.h>
 #include <sys/socket.h>
-#include <unistd.h>
 #endif
-  
+
 #include <netdb.h>  // getaddrinfo etc.
 #include <stdio.h>  // snprintf
 #include <stdlib.h> // malloc, free
@@ -157,57 +157,86 @@ void debug_print_buffer(int level, char *buf, size_t buf_len) {
   }
 }
 
-uint64_t get_self_clock_id() {
-  // make up a clock ID based on an interfaces' MAC
-  unsigned char local_clock_id[8];
-  int len = 0;
+// pass in an array of bytes and a max_length, or a max length of 0 for unlimited
+// the actual size will be returned
+
+int get_device_id(uint8_t *id, int *int_length) {
+  int max_length = *int_length;
+  int response = -1;
   struct ifaddrs *ifaddr = NULL;
   struct ifaddrs *ifa = NULL;
-  int status;
-  if ((status = getifaddrs(&ifaddr) == -1)) {
-    die("getifaddrs: %s", gai_strerror(status));
-  } else {
+  int i = 0;
+  uint8_t *t = id;
+  
+  // clear the buffer if non zero length passed in
+  for (i = 0; i < max_length; i++) {
+    *t++ = 0;
+  }
+
+  // look for a useful MAC address
+  if (getifaddrs(&ifaddr) != -1) {
+    t = id;
     int found = 0;
-    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+    
+    for (ifa = ifaddr; ((ifa != NULL) && (found == 0)); ifa = ifa->ifa_next) {
 #ifdef AF_PACKET
       if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET)) {
         struct sockaddr_ll *s = (struct sockaddr_ll *)ifa->ifa_addr;
-        if ((strcmp(ifa->ifa_name, "lo") != 0) && (found == 0)) {
-          len = s->sll_halen;
-          memcpy(local_clock_id, &s->sll_addr, len);
+        if ((strcmp(ifa->ifa_name, "lo") != 0)) {
           found = 1;
+          if ((max_length == 0) || (s->sll_halen < max_length)) {
+            max_length = s->sll_halen;
+            *int_length = max_length;
+          }
+          for (i = 0; i < max_length; i++) {
+            *t++ = s->sll_addr[i];
+          }
         }
       }
 #else
-// This AF_LINK stuff hasn't been tested!
 #ifdef AF_LINK
       struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
       if ((sdl) && (sdl->sdl_family == AF_LINK)) {
         if (sdl->sdl_type == IFT_ETHER) {
-          char *s = LLADDR(sdl);
-          int i;
-          for (i = 0; i < sdl->sdl_alen; i++) {
-            debug(1, "char %d: \"%c\".", i, *s);
-            // *t++ = (uint8_t)*s++;
-          }
           found = 1;
+          if ((max_length == 0) || (sdl->sdl_alen < max_length)) {
+            max_length = sdl->sdl_alen;
+            *int_length = max_length;
+          }
+          uint8_t *s = (uint8_t *)LLADDR(sdl);
+          for (i = 0; i < max_length; i++) {
+            *t++ = *s++;
+          }
         }
       }
-
 #endif
 #endif
+
     }
+    if (found != 0)
+      response = 0;
     freeifaddrs(ifaddr);
   }
-  // if the length of the MAC address is 6 we need to doctor it a little
-  // See Section 7.5.2.2.2 IEEE EUI-64 clockIdentity values, NOTE 2
-
-  if (len == 6) { // i.e. an EUI-48 MAC Address
-    local_clock_id[7] = local_clock_id[5];
-    local_clock_id[6] = local_clock_id[4];
-    local_clock_id[5] = local_clock_id[3];
-    local_clock_id[3] = 0xFF;
-    local_clock_id[4] = 0xFE;
+  return response;
+}
+
+
+uint64_t get_self_clock_id() {
+  // make up a clock ID based on an interface's MAC
+  int local_clock_id_size = 8; // don't exceed this
+  uint8_t local_clock_id[local_clock_id_size];
+  memset(local_clock_id,0,local_clock_id_size);
+  if (get_device_id(local_clock_id,&local_clock_id_size) == 0) {
+    // if the length of the MAC address is 6 we need to doctor it a little
+    // See Section 7.5.2.2.2 IEEE EUI-64 clockIdentity values, NOTE 2
+
+    if (local_clock_id_size == 6) { // i.e. an EUI-48 MAC Address
+      local_clock_id[7] = local_clock_id[5];
+      local_clock_id[6] = local_clock_id[4];
+      local_clock_id[5] = local_clock_id[3];
+      local_clock_id[3] = 0xFF;
+      local_clock_id[4] = 0xFE;
+    }
   }
   // convert to host byte order
   return nctoh64(local_clock_id);
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 153864f15e2772a240054929671415bb1334550c..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,27 +361,30 @@ 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: {
+                  case Follow_Up:
                     handle_follow_up(buf, recv_len, &clocks_private[the_clock], reception_time);
-                  } break;
+                    break;
+                  case Sync:
+                    handle_sync(buf, recv_len, &clocks_private[the_clock], reception_time);
+                    break;
                   default:
                     debug_print_buffer(2, buf,
                                        recv_len); // unusual messages will have debug level 1.
@@ -320,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) {
@@ -338,112 +414,126 @@ int main(int argc, char **argv) {
   return 0;
 }
 
+void send_awakening_announcement_sequence(const uint64_t clock_id, const char *clock_ip,
+                                          const int ip_family, const uint8_t priority1,
+                                          const uint8_t priority2) {
+  struct ptp_announce_message *msg;
+  size_t msg_length = sizeof(struct ptp_announce_message);
+  msg = malloc(msg_length);
+  memset((void *)msg, 0, msg_length);
+
+  uint64_t my_clock_id = get_self_clock_id();
+  msg->header.transportSpecificAndMessageID = 0x10 + Announce;
+  msg->header.reservedAndVersionPTP = 0x02;
+  msg->header.messageLength = htons(sizeof(struct ptp_announce_message));
+  msg->header.flags = htons(0x0408);
+  hcton64(my_clock_id, &msg->header.clockIdentity[0]);
+  msg->header.sourcePortID = htons(32776);
+  msg->header.controlField = 0x05;
+  msg->header.logMessagePeriod = 0xFE;
+  msg->announce.currentUtcOffset = htons(37);
+  hcton64(my_clock_id, &msg->announce.grandmasterIdentity[0]);
+  uint32_t my_clock_quality = 0xf8fe436a;
+  msg->announce.grandmasterClockQuality = htonl(my_clock_quality);
+  if (priority1 > 2) {
+    msg->announce.grandmasterPriority1 =
+        priority1 - 1; // make this announcement seem better than the clock we are about to ping
+    msg->announce.grandmasterPriority2 = priority2;
+  } else {
+    warn("Cannot select a suitable priority for pinging clock %" PRIx64 " at %s.", clock_id,
+         clock_ip);
+    msg->announce.grandmasterPriority1 = 248;
+    msg->announce.grandmasterPriority2 = 248;
+  }
+  msg->announce.timeSource = 160; // Internal Oscillator
+
+  // get the socket for the correct port -- 320 -- and family -- IPv4 or IPv6 -- to send it
+  // from.
+
+  int s = 0;
+  unsigned t;
+  for (t = 0; t < sockets_open_stuff.sockets_open; t++) {
+    if ((sockets_open_stuff.sockets[t].port == 320) &&
+        (sockets_open_stuff.sockets[t].family == ip_family))
+      s = sockets_open_stuff.sockets[t].number;
+  }
+  if (s == 0) {
+    debug(1, "sending socket not found for clock %" PRIx64 " at %s, family %s.", clock_id, clock_ip,
+          ip_family == AF_INET    ? "IPv4"
+          : ip_family == AF_INET6 ? "IPv6"
+                                  : "Unknown");
+  } else {
+    // debug(1, "Send message from socket %d.", s);
+
+    const char *portname = "320";
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_protocol = 0;
+    hints.ai_flags = AI_ADDRCONFIG;
+    struct addrinfo *res = NULL;
+    int err = getaddrinfo(clock_ip, portname, &hints, &res);
+    if (err != 0) {
+      debug(1, "failed to resolve remote socket address (err=%d)", err);
+    } else {
+      // here, we have the destination, so send it
+
+      // debug_print_buffer(1, (char *)msg, msg_length);
+      int ret = sendto(s, msg, msg_length, 0, res->ai_addr, res->ai_addrlen);
+      if (ret == -1)
+        debug(1, "result of sendto is %d.", ret);
+      debug(2, "Send awaken Announce message to clock \"%" PRIx64 "\" at %s on %s.", clock_id,
+            clock_ip, ip_family == AF_INET6 ? "IPv6" : "IPv4");
+
+      if (priority1 < 254) {
+        msg->announce.grandmasterPriority1 =
+            priority1 + 1; // make this announcement seem worse than the clock we about to ping
+      } else {
+        warn("Cannot select a suitable priority for second ping of clock %" PRIx64 " at %s.",
+             clock_id, clock_ip);
+        msg->announce.grandmasterPriority1 = 250;
+      }
+
+      msg->announce.grandmasterPriority2 = priority2;
+      usleep(150000);
+      ret = sendto(s, msg, msg_length, 0, res->ai_addr, res->ai_addrlen);
+      if (ret == -1)
+        debug(1, "result of second sendto is %d.", ret);
+      freeaddrinfo(res);
+    }
+  }
+  free(msg);
+}
+
 uint64_t broadcasting_task(uint64_t call_time, __attribute__((unused)) void *private_data) {
   clock_source_private_data *clocks_private = (clock_source_private_data *)private_data;
   int i;
   for (i = 0; i < MAX_CLOCKS; i++) {
-    if ((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(1, "Found a silent clock %" PRIx64 " at %s.", clocks_private[i].clock_id,
+
+    int is_a_master = 0;
+    int temp_client_id;
+
+    for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++)
+      if ((clocks_private->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 != 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,
             clocks_private[i].ip);
+
       // send an Announce message to attempt to waken this silent PTP clock by
       // getting it to negotiate with an apparently better clock
       // that then immediately sends another Announce message indicating that it's inferior
 
       clocks_private[i].announcements_without_followups++; // set to 4 to indicate done/parked
-
-      struct ptp_announce_message *msg;
-      size_t msg_length = sizeof(struct ptp_announce_message);
-      msg = malloc(msg_length);
-      memset((void *)msg, 0, msg_length);
-
-      uint64_t my_clock_id = get_self_clock_id();
-      msg->header.transportSpecificAndMessageID = 0x10 + Announce;
-      msg->header.reservedAndVersionPTP = 0x02;
-      msg->header.messageLength = htons(sizeof(struct ptp_announce_message));
-      msg->header.flags = htons(0x0408);
-      hcton64(my_clock_id, &msg->header.clockIdentity[0]);
-      msg->header.sourcePortID = htons(32776);
-      msg->header.controlOtherMessage = 0x05;
-      msg->header.logMessagePeriod = 0xFE;
-      msg->announce.currentUtcOffset = htons(37);
-      hcton64(my_clock_id, &msg->announce.grandmasterIdentity[0]);
-      uint32_t my_clock_quality = 0xf8fe436a;
-      msg->announce.grandmasterClockQuality = htonl(my_clock_quality);
-      if (clocks_private[i].grandmasterPriority1 > 2) {
-        msg->announce.grandmasterPriority1 =
-            clocks_private[i].grandmasterPriority1 -
-            1; // make this announcement seem better than the clock we are about to ping
-        msg->announce.grandmasterPriority2 = clocks_private[i].grandmasterPriority2;
-      } else {
-        warn("Cannot select a suitable priority for pinging clock %" PRIx64 " at %s.",
-             clocks_private[i].clock_id, clocks_private[i].ip);
-        msg->announce.grandmasterPriority1 = 248;
-        msg->announce.grandmasterPriority2 = 248;
-      }
-      msg->announce.timeSource = 160; // Internal Oscillator
-
-      // get the socket for the correct port -- 320 -- and family -- IPv4 or IPv6 -- to send it
-      // from.
-
-      int s = 0;
-      unsigned t;
-      for (t = 0; t < sockets_open_stuff.sockets_open; t++) {
-        if ((sockets_open_stuff.sockets[t].port == 320) &&
-            (sockets_open_stuff.sockets[t].family == clocks_private[i].family))
-          s = sockets_open_stuff.sockets[t].number;
-      }
-      if (s == 0) {
-        debug(1, "sending socket not found for clock %" PRIx64 " at %s, family %s.",
-              clocks_private[i].clock_id, clocks_private[i].ip,
-              clocks_private[i].family == AF_INET    ? "IPv4"
-              : clocks_private[i].family == AF_INET6 ? "IPv6"
-                                                     : "Unknown");
-      } else {
-        // debug(1, "Send message from socket %d.", s);
-
-        const char *portname = "320";
-        struct addrinfo hints;
-        memset(&hints, 0, sizeof(hints));
-        hints.ai_family = AF_UNSPEC;
-        hints.ai_socktype = SOCK_DGRAM;
-        hints.ai_protocol = 0;
-        hints.ai_flags = AI_ADDRCONFIG;
-        struct addrinfo *res = NULL;
-        int err = getaddrinfo(clocks_private[i].ip, portname, &hints, &res);
-        if (err != 0) {
-          debug(1, "failed to resolve remote socket address (err=%d)", err);
-        } else {
-          // here, we have the destination, so send it
-
-          // if (clocks_private[i].family == AF_INET6) {
-          // debug_print_buffer(1, (char *)msg, msg_length);
-          int ret = sendto(s, msg, msg_length, 0, res->ai_addr, res->ai_addrlen);
-          if (ret == -1)
-            debug(1, "result of sendto is %d.", ret);
-          debug(2, "message clock \"%" PRIx64 "\" at %s on %s.", clocks_private[i].clock_id,
-                clocks_private[i].ip, clocks_private[i].family == AF_INET6 ? "IPv6" : "IPv4");
-
-          if (clocks_private[i].grandmasterPriority1 < 254) {
-            msg->announce.grandmasterPriority1 =
-                clocks_private[i].grandmasterPriority1 +
-                1; // make this announcement seem worse than the clock we about to ping
-          } else {
-            warn("Cannot select a suitable priority for second ping of clock %" PRIx64 " at %s.",
-                 clocks_private[i].clock_id, clocks_private[i].ip);
-            msg->announce.grandmasterPriority1 = 250;
-          }
-
-          msg->announce.grandmasterPriority2 = clocks_private[i].grandmasterPriority2;
-          ret = sendto(s, msg, msg_length, 0, res->ai_addr, res->ai_addrlen);
-          if (ret == -1)
-            debug(1, "result of second sendto is %d.", ret);
-          // }
-          freeaddrinfo(res);
-        }
-      }
-      free(msg);
+      send_awakening_announcement_sequence(
+          clocks_private[i].clock_id, clocks_private[i].ip, clocks_private[i].family,
+          clocks_private[i].grandmasterPriority1, clocks_private[i].grandmasterPriority2);
     }
   }
 
diff --git a/nqptp.h b/nqptp.h
index b4c1af3c6b0d23171dffc5b5227004c868556174..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
@@ -39,4 +39,8 @@
 // Instances" -- of a "PTP Network" it wishes to monitor. This is a "timing group" in AirPlay 2
 // parlance, it seems.
 
+void send_awakening_announcement_sequence(const uint64_t clock_id, const char *clock_ip,
+                                          const int ip_family, const uint8_t priority1,
+                                          const uint8_t priority2);
+
 #endif