* Remove the ability to handle multiple instances of AirPlay-2-capable Shairport Sync on the same system -- it seems clear that clients can not use this facility.
* Greatly simplify NQPTP by only monitoring PTP information coming from the client, ignoring all other PTP information.
* Improve the accuracy of the clock by taking correction field information into account.
* In addition to trying to restart a clock that is silent, also send a restart to a clock if the clock's grandmaster appears to have stopped.
Squashed commit of the following:
Add some experimental exploratory code.
Ignore the INSTALL document. Let INSTALL always be autogenerated.
Enhanced and corrected ptp defiinitions.
Fix shm name to work with FreeBSD. Update the SHM version.
Change order of includes to avoid a compilation error in FreeBSD
Fix potential bugs in finding a clock ID in a FreeBSD device.
Only try to restart a stopped clock if it's the clock itself that has stopped and not a different grandmaster. Allow fast changes for up to one second after the start of mastership.
# http://www.gnu.org/software/autoconf
+INSTALL
autom4te.cache
/autoscan.log
/autoscan-*.log
+++ /dev/null
-Installation Instructions
-*************************
-
- Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free
-Software Foundation, Inc.
-
- Copying and distribution of this file, with or without modification,
-are permitted in any medium without royalty provided the copyright
-notice and this notice are preserved. This file is offered as-is,
-without warranty of any kind.
-
-Basic Installation
-==================
-
- Briefly, the shell command './configure && make && make install'
-should configure, build, and install this package. The following
-more-detailed instructions are generic; see the 'README' file for
-instructions specific to this package. Some packages provide this
-'INSTALL' file but do not implement all of the features documented
-below. The lack of an optional feature in a given package is not
-necessarily a bug. More recommendations for GNU packages can be found
-in *note Makefile Conventions: (standards)Makefile Conventions.
-
- The 'configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation. It uses
-those values to create a 'Makefile' in each directory of the package.
-It may also create one or more '.h' files containing system-dependent
-definitions. Finally, it creates a shell script 'config.status' that
-you can run in the future to recreate the current configuration, and a
-file 'config.log' containing compiler output (useful mainly for
-debugging 'configure').
-
- It can also use an optional file (typically called 'config.cache' and
-enabled with '--cache-file=config.cache' or simply '-C') that saves the
-results of its tests to speed up reconfiguring. Caching is disabled by
-default to prevent problems with accidental use of stale cache files.
-
- If you need to do unusual things to compile the package, please try
-to figure out how 'configure' could check whether to do them, and mail
-diffs or instructions to the address given in the 'README' so they can
-be considered for the next release. If you are using the cache, and at
-some point 'config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
- The file 'configure.ac' (or 'configure.in') is used to create
-'configure' by a program called 'autoconf'. You need 'configure.ac' if
-you want to change it or regenerate 'configure' using a newer version of
-'autoconf'.
-
- The simplest way to compile this package is:
-
- 1. 'cd' to the directory containing the package's source code and type
- './configure' to configure the package for your system.
-
- Running 'configure' might take a while. While running, it prints
- some messages telling which features it is checking for.
-
- 2. Type 'make' to compile the package.
-
- 3. Optionally, type 'make check' to run any self-tests that come with
- the package, generally using the just-built uninstalled binaries.
-
- 4. Type 'make install' to install the programs and any data files and
- documentation. When installing into a prefix owned by root, it is
- recommended that the package be configured and built as a regular
- user, and only the 'make install' phase executed with root
- privileges.
-
- 5. Optionally, type 'make installcheck' to repeat any self-tests, but
- this time using the binaries in their final installed location.
- This target does not install anything. Running this target as a
- regular user, particularly if the prior 'make install' required
- root privileges, verifies that the installation completed
- correctly.
-
- 6. You can remove the program binaries and object files from the
- source code directory by typing 'make clean'. To also remove the
- files that 'configure' created (so you can compile the package for
- a different kind of computer), type 'make distclean'. There is
- also a 'make maintainer-clean' target, but that is intended mainly
- for the package's developers. If you use it, you may have to get
- all sorts of other programs in order to regenerate files that came
- with the distribution.
-
- 7. Often, you can also type 'make uninstall' to remove the installed
- files again. In practice, not all packages have tested that
- uninstallation works correctly, even though it is required by the
- GNU Coding Standards.
-
- 8. Some packages, particularly those that use Automake, provide 'make
- distcheck', which can by used by developers to test that all other
- targets like 'make install' and 'make uninstall' work correctly.
- This target is generally not run by end users.
-
-Compilers and Options
-=====================
-
- Some systems require unusual options for compilation or linking that
-the 'configure' script does not know about. Run './configure --help'
-for details on some of the pertinent environment variables.
-
- You can give 'configure' initial values for configuration parameters
-by setting variables in the command line or in the environment. Here is
-an example:
-
- ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
- *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
- You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory. To do this, you can use GNU 'make'. 'cd' to the
-directory where you want the object files and executables to go and run
-the 'configure' script. 'configure' automatically checks for the source
-code in the directory that 'configure' is in and in '..'. This is known
-as a "VPATH" build.
-
- With a non-GNU 'make', it is safer to compile the package for one
-architecture at a time in the source code directory. After you have
-installed the package for one architecture, use 'make distclean' before
-reconfiguring for another architecture.
-
- On MacOS X 10.5 and later systems, you can create libraries and
-executables that work on multiple system types--known as "fat" or
-"universal" binaries--by specifying multiple '-arch' options to the
-compiler but only a single '-arch' option to the preprocessor. Like
-this:
-
- ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
- CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
- CPP="gcc -E" CXXCPP="g++ -E"
-
- This is not guaranteed to produce working output in all cases, you
-may have to build one architecture at a time and combine the results
-using the 'lipo' tool if you have problems.
-
-Installation Names
-==================
-
- By default, 'make install' installs the package's commands under
-'/usr/local/bin', include files under '/usr/local/include', etc. You
-can specify an installation prefix other than '/usr/local' by giving
-'configure' the option '--prefix=PREFIX', where PREFIX must be an
-absolute file name.
-
- You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files. If you
-pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
- In addition, if you use an unusual directory layout you can give
-options like '--bindir=DIR' to specify different values for particular
-kinds of files. Run 'configure --help' for a list of the directories
-you can set and what kinds of files go in them. In general, the default
-for these options is expressed in terms of '${prefix}', so that
-specifying just '--prefix' will affect all of the other directory
-specifications that were not explicitly provided.
-
- The most portable way to affect installation locations is to pass the
-correct locations to 'configure'; however, many packages provide one or
-both of the following shortcuts of passing variable assignments to the
-'make install' command line to change installation locations without
-having to reconfigure or recompile.
-
- The first method involves providing an override variable for each
-affected directory. For example, 'make install
-prefix=/alternate/directory' will choose an alternate location for all
-directory configuration variables that were expressed in terms of
-'${prefix}'. Any directories that were specified during 'configure',
-but not in terms of '${prefix}', must each be overridden at install time
-for the entire installation to be relocated. The approach of makefile
-variable overrides for each directory variable is required by the GNU
-Coding Standards, and ideally causes no recompilation. However, some
-platforms have known limitations with the semantics of shared libraries
-that end up requiring recompilation when using this method, particularly
-noticeable in packages that use GNU Libtool.
-
- The second method involves providing the 'DESTDIR' variable. For
-example, 'make install DESTDIR=/alternate/directory' will prepend
-'/alternate/directory' before all installation names. The approach of
-'DESTDIR' overrides is not required by the GNU Coding Standards, and
-does not work on platforms that have drive letters. On the other hand,
-it does better at avoiding recompilation issues, and works well even
-when some directory options were not specified in terms of '${prefix}'
-at 'configure' time.
-
-Optional Features
-=================
-
- If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving 'configure' the
-option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
-
- Some packages pay attention to '--enable-FEATURE' options to
-'configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to '--with-PACKAGE' options, where PACKAGE
-is something like 'gnu-as' or 'x' (for the X Window System). The
-'README' should mention any '--enable-' and '--with-' options that the
-package recognizes.
-
- For packages that use the X Window System, 'configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the 'configure' options '--x-includes=DIR' and
-'--x-libraries=DIR' to specify their locations.
-
- Some packages offer the ability to configure how verbose the
-execution of 'make' will be. For these packages, running './configure
---enable-silent-rules' sets the default to minimal output, which can be
-overridden with 'make V=1'; while running './configure
---disable-silent-rules' sets the default to verbose, which can be
-overridden with 'make V=0'.
-
-Particular systems
-==================
-
- On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
-is not installed, it is recommended to use the following options in
-order to use an ANSI C compiler:
-
- ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
-
-and if that doesn't work, install pre-built binaries of GCC for HP-UX.
-
- HP-UX 'make' updates targets which have the same timestamps as their
-prerequisites, which makes it generally unusable when shipped generated
-files such as 'configure' are involved. Use GNU 'make' instead.
-
- On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
-parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
-workaround. If GNU CC is not installed, it is therefore recommended to
-try
-
- ./configure CC="cc"
-
-and if that doesn't work, try
-
- ./configure CC="cc -nodtk"
-
- On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
-directory contains several dysfunctional programs; working variants of
-these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
-in your 'PATH', put it _after_ '/usr/bin'.
-
- On Haiku, software installed for all users goes in '/boot/common',
-not '/usr/local'. It is recommended to use the following options:
-
- ./configure --prefix=/boot/common
-
-Specifying the System Type
-==========================
-
- There may be some features 'configure' cannot figure out
-automatically, but needs to determine by the type of machine the package
-will run on. Usually, assuming the package is built to be run on the
-_same_ architectures, 'configure' can figure that out, but if it prints
-a message saying it cannot guess the machine type, give it the
-'--build=TYPE' option. TYPE can either be a short name for the system
-type, such as 'sun4', or a canonical name which has the form:
-
- CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
- OS
- KERNEL-OS
-
- See the file 'config.sub' for the possible values of each field. If
-'config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
- If you are _building_ compiler tools for cross-compiling, you should
-use the option '--target=TYPE' to select the type of system they will
-produce code for.
-
- If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with '--host=TYPE'.
-
-Sharing Defaults
-================
-
- If you want to set default values for 'configure' scripts to share,
-you can create a site shell script called 'config.site' that gives
-default values for variables like 'CC', 'cache_file', and 'prefix'.
-'configure' looks for 'PREFIX/share/config.site' if it exists, then
-'PREFIX/etc/config.site' if it exists. Or, you can set the
-'CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all 'configure' scripts look for a site script.
-
-Defining Variables
-==================
-
- Variables not defined in a site shell script can be set in the
-environment passed to 'configure'. However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost. In order to avoid this problem, you should set
-them in the 'configure' command line, using 'VAR=value'. For example:
-
- ./configure CC=/usr/local2/bin/gcc
-
-causes the specified 'gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
-Autoconf limitation. Until the limitation is lifted, you can use this
-workaround:
-
- CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-'configure' Invocation
-======================
-
- 'configure' recognizes the following options to control how it
-operates.
-
-'--help'
-'-h'
- Print a summary of all of the options to 'configure', and exit.
-
-'--help=short'
-'--help=recursive'
- Print a summary of the options unique to this package's
- 'configure', and exit. The 'short' variant lists options used only
- in the top level, while the 'recursive' variant lists options also
- present in any nested packages.
-
-'--version'
-'-V'
- Print the version of Autoconf used to generate the 'configure'
- script, and exit.
-
-'--cache-file=FILE'
- Enable the cache: use and save the results of the tests in FILE,
- traditionally 'config.cache'. FILE defaults to '/dev/null' to
- disable caching.
-
-'--config-cache'
-'-C'
- Alias for '--cache-file=config.cache'.
-
-'--quiet'
-'--silent'
-'-q'
- Do not print messages saying which checks are being made. To
- suppress all normal output, redirect it to '/dev/null' (any error
- messages will still be shown).
-
-'--srcdir=DIR'
- Look for the package's source code in directory DIR. Usually
- 'configure' can determine that directory automatically.
-
-'--prefix=DIR'
- Use DIR as the installation prefix. *note Installation Names:: for
- more details, including other options available for fine-tuning the
- installation locations.
-
-'--no-create'
-'-n'
- Run the configure checks, but stop before creating any output
- files.
-
-'configure' also accepts some other, not widely useful, options. Run
-'configure --help' for more details.
/*
* 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
/*
* 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
/*
* 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
#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];
return response;
}
-void manage_clock_sources(uint64_t reception_time, clock_source_private_data *clocks_private_info) {
- debug(3, "manage_clock_sources");
- int i;
-
- // do a garbage collect for clock records no longer in use
- for (i = 0; i < MAX_CLOCKS; i++) {
- // only if its in use and not a timing peer... don't need a mutex to check
- // TODO -- check all clients to see if it's in use
- if ((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) {
- int clock_is_a_timing_peer_somewhere = 0;
- int temp_client_id;
- for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
- if ((clocks_private_info[i].client_flags[temp_client_id] & (1 << clock_is_a_timing_peer)) !=
- 0) {
- clock_is_a_timing_peer_somewhere = 1;
- }
- }
- if (clock_is_a_timing_peer_somewhere == 0) {
- int64_t time_since_last_use = reception_time - clocks_private_info[i].time_of_last_use;
- // using a sync timeout to determine when to drop the record...
- // the following give the sync receipt time in whole seconds
- // depending on the aPTPinitialLogSyncInterval and the aPTPsyncReceiptTimeout
- int64_t syncTimeout = (1 << (32 + aPTPinitialLogSyncInterval));
- syncTimeout = syncTimeout * aPTPsyncReceiptTimeout;
- syncTimeout = syncTimeout >> 32;
- // seconds to nanoseconds
- syncTimeout = syncTimeout * 1000000000;
- if (time_since_last_use > syncTimeout) {
- uint32_t old_flags = clocks_private_info[i].flags;
- debug(2, "delete record for: %s.", &clocks_private_info[i].ip);
- memset(&clocks_private_info[i], 0, sizeof(clock_source_private_data));
- if (old_flags != 0) {
- update_master(0); // TODO -- won't be needed
- }
- }
- }
- }
- }
-}
-
-// check all the entries in the clock array and mark all those that
-// belong to ourselves
-
-void update_clock_self_identifications(clock_source_private_data *clocks_private_info) {
- // first, turn off all the self-id flags
- int i;
- for (i = 0; i < MAX_CLOCKS; i++) {
- clocks_private_info[i].flags &= ~(1 << clock_is_one_of_ours);
- }
-
- struct ifaddrs *ifap, *ifa;
- void *addr = NULL;
- short family;
- int response = getifaddrs(&ifap);
- if (response == 0) {
- for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- struct sockaddr *my_ifa_addr = ifa->ifa_addr;
- if (my_ifa_addr) {
- family = my_ifa_addr->sa_family;
-#ifdef AF_INET6
- if (family == AF_INET6) {
- struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)my_ifa_addr;
- addr = &(sa6->sin6_addr);
- }
-#endif
- if (family == AF_INET) {
- struct sockaddr_in *sa4 = (struct sockaddr_in *)my_ifa_addr;
- addr = &(sa4->sin_addr);
- }
- char ip_string[64];
- memset(ip_string, 0, sizeof(ip_string));
- if (addr != NULL)
- inet_ntop(family, addr, ip_string, sizeof(ip_string));
- if (strlen(ip_string) != 0) {
- // now set the clock_is_one_of_ours flag of any clock with this ip
- for (i = 0; i < MAX_CLOCKS; i++) {
- if (strcasecmp(ip_string, clocks_private_info[i].ip) == 0) {
- debug(2, "found an entry for one of our clocks");
- clocks_private_info[i].flags |= (1 << clock_is_one_of_ours);
- }
- }
- }
- }
- }
- freeifaddrs(ifap);
- } else {
- debug(1, "getifaddrs error - %s.", strerror(errno));
- }
-}
-
-int uint32_cmp(uint32_t a, uint32_t b, const char *cause) {
- // returns -1 if a is less than b, 0 if a = b, +1 if a is greater than b
- if (a == b) {
- return 0;
- } else {
- debug(2, "Best Master Clock algorithm deciding factor: %s. Values: %u, %u.", cause, a, b);
- if (a < b)
- return -1;
- else
- return 1;
- }
-}
-
-int uint64_cmp(uint64_t a, uint64_t b, const char *cause) {
- // returns -1 if a is less than b, 0 if a = b, +1 if a is greater than b
- if (a == b) {
- return 0;
- } else {
- debug(2, "Best Master Clock algorithm deciding factor: %s. Values: %" PRIx64 ", %" PRIx64 ".",
- cause, a, b);
- if (a < b)
- return -1;
- else
- return 1;
- }
-}
-
-void update_master(int client_id) {
-
- // This implements the IEEE 1588-2008 best master clock algorithm.
-
- // However, since nqptp is not a ptp clock, some of it doesn't apply.
- // Specifically, the Identity of Receiver stuff doesn't apply, since the
- // program is merely monitoring Announce message data and isn't a PTP clock itself
- // and thus does not have any kind or receiver identity itself.
-
- // Clock information coming from the same clock over IPv4 and IPv6 should have different
- // port numbers.
-
- // Figure 28 can be therefore be simplified considerably:
-
- // Since nqptp can not be a receiver, and since nqptp can not originate a clock
- // (and anyway nqptp filters out packets coming from self)
- // we can do a single comparison of stepsRemoved and pick the shorter, if any.
-
- // Figure 28 reduces to checking steps removed and then, if necessary, checking identities.
- // If we see two identical sets of information, it is an error,
- // but we leave things as they are.
- int old_master = -1;
- // find the current master clock if there is one and turn off all mastership
- int i;
- for (i = 0; i < MAX_CLOCKS; i++) {
- if ((clocks_private[i].client_flags[client_id] & (1 << clock_is_master)) != 0)
- if (old_master == -1)
- old_master = i; // find old master
- clocks_private[i].client_flags[client_id] &= ~(1 << clock_is_master); // turn them all off
- }
-
- int best_so_far = -1;
- int timing_peer_count = 0;
- uint32_t clock_specific_acceptance_mask = (1 << clock_is_announced);
- uint32_t client_specific_acceptance_mask = (1 << clock_is_a_timing_peer);
- for (i = 0; i < MAX_CLOCKS; i++) {
- if (((clocks_private[i].flags & clock_specific_acceptance_mask) ==
- clock_specific_acceptance_mask) &&
- ((clocks_private[i].client_flags[client_id] & client_specific_acceptance_mask) ==
- client_specific_acceptance_mask)) {
- // found a possible clock candidate
- timing_peer_count++;
- int outcome;
- if (best_so_far == -1) {
- best_so_far = i;
- } else {
- // Do the data set comparison detailed in Figure 27 and Figure 28 on pp89-90
- if (clocks_private[i].grandmasterIdentity ==
- clocks_private[best_so_far].grandmasterIdentity) {
- // Do the relevant part of Figure 28:
- outcome = uint32_cmp(clocks_private[i].stepsRemoved,
- clocks_private[best_so_far].stepsRemoved, "steps removed");
- // we need to check the portIdentify, which is the clock_id and the clock_port_number
- if (outcome == 0)
- outcome = uint64_cmp(clocks_private[i].clock_id, clocks_private[best_so_far].clock_id,
- "clock id");
- if (outcome == 0)
- outcome =
- uint32_cmp(clocks_private[i].clock_port_number,
- clocks_private[best_so_far].clock_port_number, "clock port number");
- if (outcome == 0) {
- debug(1,
- "Best Master Clock algorithm: two separate but identical potential clock "
- "masters: %" PRIx64 ".",
- clocks_private[best_so_far].clock_id);
- }
-
- } else {
- outcome =
- uint32_cmp(clocks_private[i].grandmasterPriority1,
- clocks_private[best_so_far].grandmasterPriority1, "grandmasterPriority1");
- if (outcome == 0)
- outcome = uint32_cmp(clocks_private[i].grandmasterClass,
- clocks_private[best_so_far].grandmasterClass, "grandmasterClass");
- if (outcome == 0)
- outcome =
- uint32_cmp(clocks_private[i].grandmasterAccuracy,
- clocks_private[best_so_far].grandmasterAccuracy, "grandmasterAccuracy");
- if (outcome == 0)
- outcome =
- uint32_cmp(clocks_private[i].grandmasterVariance,
- clocks_private[best_so_far].grandmasterVariance, "grandmasterVariance");
- if (outcome == 0)
- outcome = uint32_cmp(clocks_private[i].grandmasterPriority2,
- clocks_private[best_so_far].grandmasterPriority2,
- "grandmasterPriority2");
- if (outcome == 0)
- // this can't fail, as it's a condition of entering this section that they are different
- outcome =
- uint64_cmp(clocks_private[i].grandmasterIdentity,
- clocks_private[best_so_far].grandmasterIdentity, "grandmasterIdentity");
- }
- if (outcome == -1)
- best_so_far = i;
- }
- }
- }
-// if ((best_so_far == -1) || (best_so_far != old_master)) {
- if (best_so_far == -1) {
- // no master clock
- // if (old_master != -1) {
- // but there was a master clock, so remove it
- debug(2, "Remove master clock information from interface %s.", get_client_name(client_id));
- update_master_clock_info(client_id, 0, NULL, 0, 0, 0);
- //}
- }
- if (best_so_far == -1) {
- if (timing_peer_count == 0)
- debug(2, "empty timing peer group ");
- else
- debug(1, "no master clock!");
- } else {
- // we mark the master clock we found
- clocks_private[best_so_far].client_flags[client_id] |= (1 << clock_is_master);
- }
-}
-
void update_master_clock_info(int client_id, uint64_t master_clock_id, const char *ip,
uint64_t local_time, uint64_t local_to_master_offset,
uint64_t mastership_start_time) {
// 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");
+}
/*
* 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;
// 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);
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
#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) {
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)
}
}
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)) {
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.");
}
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;
}
}
debug(2, "Sync received before announcement -- discarded.");
} else {
if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_sync_message))) {
- int is_a_master = 0;
- int temp_client_id;
- for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++)
- if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_master)) != 0)
- is_a_master = 1;
-
- // only process it if it's a master somewhere...
-
- if (is_a_master) {
// debug_print_buffer(1, buf, recv_len);
struct ptp_sync_message *msg = (struct ptp_sync_message *)buf;
debug(1, "Sync correction field is non-zero: %" PRId64 " ns.", correction_field);
correction_field = correction_field / 65536; // might be signed
- }
} else {
debug(1, "Sync message is too small to be valid.");
}
} else {
clock_private_info->announcements_without_followups = 0;
if ((recv_len >= 0) && ((size_t)recv_len >= sizeof(struct ptp_follow_up_message))) {
- int is_a_master = 0;
- int temp_client_id;
- for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++)
- if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_master)) != 0)
- is_a_master = 1;
-
- // only process it if it's a master somewhere...
-
- if (is_a_master) {
// debug_print_buffer(1, buf, recv_len);
struct ptp_follow_up_message *msg = (struct ptp_follow_up_message *)buf;
uint16_t seconds_hi = nctohs(&msg->follow_up.preciseOriginTimestamp[0]);
clock_private_info->clock_id, 0.000000001 * duration_of_mastership);
int64_t wait_limit = 62;
wait_limit = wait_limit * 1000000000;
- if (duration_of_mastership <= wait_limit) {
- debug(2,
+ // only try to restart a grandmaster clock on the clock itself.
+ if ((duration_of_mastership <= wait_limit) && (clock_private_info->clock_id == clock_private_info->grandmasterIdentity)) {
+ debug(1,
"Attempt to start a stopped clock %" PRIx64
", at follow_up_number %u at IP %s.",
clock_private_info->clock_id, clock_private_info->follow_up_number,
// Follow-ups don't always come in at 125 ms intervals, especially after a discontinuity
// Delays makes the offsets smaller than they should be, which is quickly
// allowed for.
+
+ int64_t mastership_time = reception_time - clock_private_info->mastership_start_time;
+ if (clock_private_info->mastership_start_time == 0)
+ mastership_time = 0;
if ((clock_private_info->previous_offset_time != 0) && (jitter > -10000000)) {
if (jitter < 0) {
- if (clock_private_info->follow_up_number <
- (5 * 8)) // at the beginning (8 samples per second)
+ if (mastership_time < 1000000000) // at the beginning
smoothed_offset = clock_private_info->previous_offset + jitter / 16;
else
smoothed_offset = clock_private_info->previous_offset + jitter / 64;
- } else if (clock_private_info->follow_up_number <
- (5 * 8)) // at the beginning (8 samples per second)
+ } else if (mastership_time < 1000000000) // at the beginning
smoothed_offset =
clock_private_info->previous_offset + jitter / 1; // accept positive changes quickly
else
int temp_client_id;
for (temp_client_id = 0; temp_client_id < MAX_CLIENTS; temp_client_id++) {
if ((clock_private_info->client_flags[temp_client_id] & (1 << clock_is_master)) != 0) {
- debug(2,
- "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
- ", smoothed offset: %" PRIx64 ". Raw Precise Origin Timestamp: %" PRIx64
- ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
- "%u. Source: %s",
- clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
- smoothed_offset, preciseOriginTimestamp, 0.000001 * time_since_previous_offset,
- ntohs(msg->header.sequenceId), clock_private_info->follow_up_number,
- clock_private_info->ip);
-
- debug(2, "clock_is_master -- updating master clock info for client \"%s\"",
- get_client_name(temp_client_id));
- update_master_clock_info(temp_client_id, clock_private_info->clock_id,
- (const char *)&clock_private_info->ip, reception_time,
- smoothed_offset, clock_private_info->mastership_start_time);
+ // recalculate mastership_time because it might have been zeroed.
+ mastership_time = reception_time - clock_private_info->mastership_start_time;
+ if (clock_private_info->mastership_start_time == 0)
+ mastership_time = 0;
+ if (mastership_time > 200000000) {
+ int64_t delta = smoothed_offset - offset;
+ debug(2,
+ "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
+ ", smoothed offset: %" PRIx64 ". Smoothed Offset - Offset: %10.3f. Raw Precise Origin Timestamp: %" PRIx64
+ ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
+ "%u. Source: %s",
+ clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
+ smoothed_offset, 0.000001 * delta, preciseOriginTimestamp, 0.000001 * time_since_previous_offset,
+ ntohs(msg->header.sequenceId), clock_private_info->follow_up_number,
+ clock_private_info->ip);
+
+ debug(2, "clock_is_master -- updating master clock info for client \"%s\"",
+ get_client_name(temp_client_id));
+ update_master_clock_info(temp_client_id, clock_private_info->grandmasterIdentity,
+ (const char *)&clock_private_info->ip, reception_time,
+ smoothed_offset, clock_private_info->mastership_start_time);
+ }
}
}
- }
+
+ int64_t delta = smoothed_offset - offset;
+ debug(2,
+ "Clock %" PRIx64 ", grandmaster %" PRIx64 ". Offset: %" PRIx64
+ ", smoothed offset: %" PRIx64 ". Smoothed Offset - Offset: %10.3f. Raw Precise Origin Timestamp: %" PRIx64
+ ". Time since previous offset: %8.3f milliseconds. ID: %5u, Follow_Up Number: "
+ "%u. Source: %s",
+ clock_private_info->clock_id, clock_private_info->grandmasterIdentity, offset,
+ smoothed_offset, 0.000001 * delta, preciseOriginTimestamp, 0.000001 * time_since_previous_offset,
+ ntohs(msg->header.sequenceId), clock_private_info->follow_up_number,
+ clock_private_info->ip);
+
+ new_update_master_clock_info(clock_private_info->grandmasterIdentity,
+ (const char *)&clock_private_info->ip, reception_time,
+ smoothed_offset, clock_private_info->mastership_start_time);
+
+ // now do some quick calculations on the possible "Universal Time"
+ // debug_print_buffer(1, "", buf, recv_len);
+ uint8_t *tlv = (uint8_t *)&msg->follow_up.tlvs[0];
+ uint8_t *lastGmPhaseChange = tlv + 16;
+ uint64_t lpt = nctoh64(lastGmPhaseChange + 4);
+ uint64_t last_tlv_clock = nctoh64((uint8_t *)buf + 86);
+ uint64_t huh = offset - lpt;
+ debug_print_buffer(2, buf, (size_t) recv_len);
+ debug(2, "%" PRIx64 ", %" PRIx64 ", %s, Origin: %016" PRIx64 ", LPT: %016" PRIx64 ", Offset: %016" PRIx64 ", Universal Offset: %016" PRIx64 ", packet length: %u.", clock_private_info->clock_id, last_tlv_clock, hex_string(lastGmPhaseChange,12), preciseOriginTimestamp, lpt, offset, huh, recv_len);
+ // debug(1,"Clock: %" PRIx64 ", UT: %016" PRIx64 ", correctedPOT: %016" PRIx64 ", part of lastGMPhaseChange: %016" PRIx64 ".", packet_clock_id, correctedPOT - lpt, correctedPOT, lpt);
+
} else {
debug(1, "Follow_Up message is too small to be valid.");
}
/*
* 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
/*
* 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
// 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
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
};
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
// 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
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];
#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
/*
* 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
/*
* 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
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);
}
}
}
-
+
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));
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;
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:
}
}
}
- 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) {
msg->header.flags = htons(0x0408);
hcton64(my_clock_id, &msg->header.clockIdentity[0]);
msg->header.sourcePortID = htons(32776);
- msg->header.controlOtherMessage = 0x05;
+ msg->header.controlField = 0x05;
msg->header.logMessagePeriod = 0xFE;
msg->announce.currentUtcOffset = htons(37);
hcton64(my_clock_id, &msg->announce.grandmasterIdentity[0]);
is_a_master = 1;
// only process it if it's a master somewhere...
- if ((is_a_master != 0) && (clocks_private[i].announcements_without_followups == 3) &&
- // (clocks_private[i].follow_up_number == 0) && // only check at the start
- ((clocks_private[i].flags & (1 << clock_is_one_of_ours)) == 0)) {
- debug(2,
+ if ((is_a_master != 0) && (clocks_private[i].announcements_without_followups == 3)) {
+ debug(1,
"Attempt to awaken a silent clock %" PRIx64
", index %u, at follow_up_number %u at IP %s.",
clocks_private[i].clock_id, i, clocks_private[i].follow_up_number,
/*
* 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