Add a second servo that provides samples to other processes in order to control the clock. The chrony SOCK refclock uses a Unix domain socket instead of a shared memory segment.
The main advantage over the NTP SHM refclock is better security as the socket can be located in a directory with restricted access, while the shared memory segment (using a predictable key) can be created by untrusted users or applications if they can run before ptp4l/phc2sys and chronyd/ntpd, similarly to the issue with binding to an unprivileged TCP/UDP port. Signed-off-by: Miroslav Lichvar <mlich...@redhat.com> --- config.c | 2 + configs/default.cfg | 1 + makefile | 2 +- phc2sys.8 | 9 ++- phc2sys.c | 3 + ptp4l.8 | 12 ++-- refclock_sock.c | 163 ++++++++++++++++++++++++++++++++++++++++++++ refclock_sock.h | 26 +++++++ servo.c | 4 ++ servo.h | 1 + 10 files changed, 216 insertions(+), 7 deletions(-) create mode 100644 refclock_sock.c create mode 100644 refclock_sock.h diff --git a/config.c b/config.c index 08e3346..d3088e7 100644 --- a/config.c +++ b/config.c @@ -144,6 +144,7 @@ static struct config_enum clock_servo_enu[] = { { "linreg", CLOCK_SERVO_LINREG }, { "ntpshm", CLOCK_SERVO_NTPSHM }, { "nullf", CLOCK_SERVO_NULLF }, + { "refclock_sock", CLOCK_SERVO_REFCLOCK_SOCK }, { NULL, 0 }, }; @@ -232,6 +233,7 @@ struct config_item config_tab[] = { PORT_ITEM_INT("boundary_clock_jbod", 0, 0, 1), PORT_ITEM_ENU("BMCA", BMCA_PTP, bmca_enu), GLOB_ITEM_INT("check_fup_sync", 0, 0, 1), + GLOB_ITEM_STR("refclock_sock_address", "/var/run/chrony/refclock.sock"), GLOB_ITEM_INT("clientOnly", 0, 0, 1), GLOB_ITEM_INT("clockAccuracy", 0xfe, 0, UINT8_MAX), GLOB_ITEM_INT("clockClass", 248, 0, UINT8_MAX), diff --git a/configs/default.cfg b/configs/default.cfg index 1b5b806..f5a2bb1 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -78,6 +78,7 @@ first_step_threshold 0.00002 max_frequency 900000000 clock_servo pi sanity_freq_limit 200000000 +refclock_sock_address /var/run/chrony/refclock.sock ntpshm_segment 0 msg_interval_request 0 servo_num_offset_values 10 diff --git a/makefile b/makefile index ba3fb38..0f8f185 100644 --- a/makefile +++ b/makefile @@ -24,7 +24,7 @@ CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) LDLIBS = -lm -lrt -pthread $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc FILTERS = filter.o mave.o mmedian.o -SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o +SERVOS = linreg.o ntpshm.o nullf.o pi.o refclock_sock.o servo.o TRANSP = raw.o transport.o udp.o udp6.o uds.o TS2PHC = ts2phc.o lstab.o nmea.o serial.o sock.o ts2phc_generic_pps_source.o \ ts2phc_nmea_pps_source.o ts2phc_phc_pps_source.o ts2phc_pps_sink.o ts2phc_pps_source.o diff --git a/phc2sys.8 b/phc2sys.8 index 9825ec7..69fd734 100644 --- a/phc2sys.8 +++ b/phc2sys.8 @@ -125,8 +125,8 @@ option. This option may be given up to 128 times. .BI \-E " servo" Specify which clock servo should be used. Valid values are pi for a PI controller, linreg for an adaptive controller using linear regression, and -ntpshm for the NTP SHM reference clock to allow another process to synchronize -the local clock. +ntpshm and refclock_sock for the NTP SHM and chrony SOCK reference clocks +respectively to allow another process to synchronize the local clock. The default is pi. .TP .BI \-P " kp" @@ -382,6 +382,11 @@ Same as option .B \-F (see above). +.TP +.B chrony_sock_address +The address of the chronyd's UNIX domain socket configured as a SOCK refclock +to be used by the sock servo. The default is /var/run/chrony/refclock.sock. + .TP .B ntpshm_segment The number of the SHM segment used by ntpshm servo. The default is 0. diff --git a/phc2sys.c b/phc2sys.c index 88ed00c..836e63c 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -1140,6 +1140,9 @@ int main(int argc, char *argv[]) } else if (!strcasecmp(optarg, "ntpshm")) { config_set_int(cfg, "clock_servo", CLOCK_SERVO_NTPSHM); + } else if (!strcasecmp(optarg, "refclock_sock")) { + config_set_int(cfg, "clock_servo", + CLOCK_SERVO_REFCLOCK_SOCK); } else { fprintf(stderr, "invalid servo name %s\n", optarg); diff --git a/ptp4l.8 b/ptp4l.8 index d2a038e..52f11d6 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -527,10 +527,10 @@ The default is 0 (disabled). .B clock_servo The servo which is used to synchronize the local clock. Valid values are "pi" for a PI controller, "linreg" for an adaptive controller -using linear regression, "ntpshm" for the NTP SHM reference clock to -allow another process to synchronize the local clock, and "nullf" -for a servo that -always dials frequency offset zero (for use in SyncE nodes). +using linear regression, "ntpshm" and "refclock_sock" for the NTP SHM and +chrony SOCK reference clocks respectively to allow another process to +synchronize the local clock, and "nullf" for a servo that always dials +frequency offset zero (for use in SyncE nodes). The default is "pi." .TP .B clock_type @@ -630,6 +630,10 @@ the clock before the delay is measured using the E2E or P2P delay mechanism. If set to 0, the clock will not be updated until the delay is measured. The default is 0. .TP +.B chrony_sock_address +The address of the chronyd's UNIX domain socket configured as a SOCK refclock +to be used by the sock servo. The default is /var/run/chrony/refclock.sock. +.TP .B ntpshm_segment The number of the SHM segment used by ntpshm servo. The default is 0. diff --git a/refclock_sock.c b/refclock_sock.c new file mode 100644 index 0000000..397da52 --- /dev/null +++ b/refclock_sock.c @@ -0,0 +1,163 @@ +/** + * @file refclock_sock.c + * @brief Implements a servo providing samples over Unix domain socket. + * @note Copyright (C) 2023 Miroslav Lichvar <mlich...@redhat.com> + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#include "refclock_sock.h" +#include "config.h" +#include "print.h" +#include "servo_private.h" + +#define LEAP_NORMAL 0 +#define LEAP_INSERT 1 +#define LEAP_DELETE 2 +#define SOCK_MAGIC 0x534f434b + +/* Copied from chrony-3.2/refclock_sock.c */ +struct sock_sample { + /* Time of the measurement (system time) */ + struct timeval tv; + + /* Offset between the true time and the system time (in seconds) */ + double offset; + + /* Non-zero if the sample is from a PPS signal, i.e. another source + is needed to obtain seconds */ + int pulse; + + /* 0 - normal, 1 - insert leap second, 2 - delete leap second */ + int leap; + + /* Padding, ignored */ + int _pad; + + /* Protocol identifier (0x534f434b) */ + int magic; +}; + +struct sock_servo { + struct servo servo; + int sock_fd; + int leap; +}; + +static void refclock_sock_destroy(struct servo *servo) +{ + struct sock_servo *s = container_of(servo, struct sock_servo, servo); + free(s); +} + +static double refclock_sock_sample(struct servo *servo, + int64_t offset, + uint64_t local_ts, + double weight, + enum servo_state *state) +{ + struct sock_servo *s = container_of(servo, struct sock_servo, servo); + struct sock_sample sample; + + memset(&sample, 0, sizeof(sample)); + sample.tv.tv_sec = local_ts / 1000000000ULL; + sample.tv.tv_usec = local_ts % 1000000000ULL / 1000U; + sample.offset = -offset / 1e9; + sample.magic = SOCK_MAGIC; + + switch (s->leap) { + case -1: + sample.leap = LEAP_DELETE; + break; + case 1: + sample.leap = LEAP_INSERT; + break; + default: + sample.leap = LEAP_NORMAL; + } + + if (send(s->sock_fd, &sample, sizeof sample, 0) != sizeof sample) { + pr_err("refclock_sock: send failed: %m"); + return 0; + } + + *state = SERVO_UNLOCKED; + return 0.0; +} + +static void refclock_sock_sync_interval(struct servo *servo, double interval) +{ +} + +static void refclock_sock_reset(struct servo *servo) +{ +} + +static void refclock_sock_leap(struct servo *servo, int leap) +{ + struct sock_servo *s = container_of(servo, struct sock_servo, servo); + + s->leap = leap; +} + +struct servo *refclock_sock_servo_create(struct config *cfg) +{ + char *addr = config_get_string(cfg, NULL, "refclock_sock_address"); + struct sockaddr_un sa; + struct sock_servo *s; + int i; + + s = calloc(1, sizeof(*s)); + if (!s) + return NULL; + + s->servo.destroy = refclock_sock_destroy; + s->servo.sample = refclock_sock_sample; + s->servo.sync_interval = refclock_sock_sync_interval; + s->servo.reset = refclock_sock_reset; + s->servo.leap = refclock_sock_leap; + + s->sock_fd = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s->sock_fd < 0) { + pr_err("refclock_sock: failed to create socket: %m"); + free(s); + return NULL; + } + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path, addr, sizeof(sa.sun_path) - 1); + + /* Wait up to 1 second for the server socket to be created */ + for (i = 10; i >= 0; i--) { + if (!connect(s->sock_fd, (struct sockaddr *)&sa, sizeof(sa))) + break; + if (i > 0) { + usleep(100000); + continue; + } + + pr_err("refclock_sock: connect failed: %m"); + close(s->sock_fd); + free(s); + return NULL; + } + + return &s->servo; +} diff --git a/refclock_sock.h b/refclock_sock.h new file mode 100644 index 0000000..44ed226 --- /dev/null +++ b/refclock_sock.h @@ -0,0 +1,26 @@ +/** + * @file refclock_sock.h + * @note Copyright (C) 2023 Miroslav Lichvar <mlich...@redhat.com> + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef HAVE_REFCLOCK_SOCK_H +#define HAVE_REFCLOCK_SOCK_H + +#include "servo.h" + +struct servo *refclock_sock_servo_create(struct config *cfg); + +#endif diff --git a/servo.c b/servo.c index 6ba7cb4..daaa41c 100644 --- a/servo.c +++ b/servo.c @@ -24,6 +24,7 @@ #include "ntpshm.h" #include "nullf.h" #include "pi.h" +#include "refclock_sock.h" #include "servo_private.h" #include "print.h" @@ -51,6 +52,9 @@ struct servo *servo_create(struct config *cfg, enum servo_type type, case CLOCK_SERVO_NULLF: servo = nullf_servo_create(); break; + case CLOCK_SERVO_REFCLOCK_SOCK: + servo = refclock_sock_servo_create(cfg); + break; default: return NULL; } diff --git a/servo.h b/servo.h index 6cedb66..e154692 100644 --- a/servo.h +++ b/servo.h @@ -35,6 +35,7 @@ enum servo_type { CLOCK_SERVO_LINREG, CLOCK_SERVO_NTPSHM, CLOCK_SERVO_NULLF, + CLOCK_SERVO_REFCLOCK_SOCK, }; /** -- 2.39.0 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel