Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package chrony for openSUSE:Factory checked in at 2025-07-10 23:14:56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/chrony (Old) and /work/SRC/openSUSE:Factory/.chrony.new.7373 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "chrony" Thu Jul 10 23:14:56 2025 rev:47 rq:1291542 version:4.7 Changes: -------- --- /work/SRC/openSUSE:Factory/chrony/chrony.changes 2025-02-13 18:37:47.877063244 +0100 +++ /work/SRC/openSUSE:Factory/.chrony.new.7373/chrony.changes 2025-07-10 23:15:10.600859568 +0200 @@ -1,0 +2,26 @@ +Wed Jul 2 17:42:22 UTC 2025 - Reinhard Max <m...@suse.com> + +- Update to version 4.5: + * Add opencommands directive to select remote monitoring + commands + * Add interval option to driftfile directive + * Add waitsynced and waitunsynced options to local directive + * Add sanity checks for integer values in configuration + * Add support for systemd Type=notify service + * Add RTC refclock driver + * Allow PHC refclock to be specified with network interface name + * Don’t require multiple refclock samples per poll to simplify + filter configuration + * Keep refclock reachable when dropping samples with large delay + * Improve quantile-based filtering to adapt faster to larger + delay + * Improve logging of selection failures + * Detect clock interference from other processes + * Try to reopen message log (-l option) on cyclelogs command + * Fix sourcedir reloading to not multiply sources + * Fix tracking offset after failed clock step + * Drop support for NTS with Nettle < 3.6 and GnuTLS < 3.6.14 + * Drop support for building without POSIX threads +- Update clknetsim to snapshot 530d1a5. + +------------------------------------------------------------------- Old: ---- chrony-4.6.1.tar.gz chrony-4.6.1.tar.gz.sig clknetsim-633a0be.tar.gz New: ---- chrony-4.7.tar.gz chrony-4.7.tar.gz.sig clknetsim-530d1a5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ chrony.spec ++++++ --- /var/tmp/diff_new_pack.H2SM0p/_old 2025-07-10 23:15:11.520897894 +0200 +++ /var/tmp/diff_new_pack.H2SM0p/_new 2025-07-10 23:15:11.520897894 +0200 @@ -33,7 +33,7 @@ %bcond_without testsuite %define _systemdutildir %(pkg-config --variable systemdutildir systemd) -%global clknetsim_ver 633a0be +%global clknetsim_ver 530d1a5 #Compat macro for new _fillupdir macro introduced in Nov 2017 %if ! %{defined _fillupdir} %define _fillupdir %{_localstatedir}/adm/fillup-templates @@ -41,7 +41,7 @@ %define chrony_helper %{_libexecdir}/chrony/helper %define chrony_rundir %{_rundir}/%{name} Name: chrony -Version: 4.6.1 +Version: 4.7 Release: 0 Summary: System Clock Synchronization Client and Server License: GPL-2.0-only ++++++ chrony-4.6.1.tar.gz -> chrony-4.7.tar.gz ++++++ ++++ 9612 lines of diff (skipped) ++++++ chrony-config.patch ++++++ --- /var/tmp/diff_new_pack.H2SM0p/_old 2025-07-10 23:15:11.740907059 +0200 +++ /var/tmp/diff_new_pack.H2SM0p/_new 2025-07-10 23:15:11.744907225 +0200 @@ -1,7 +1,7 @@ --- examples/chrony.conf.example3.orig +++ examples/chrony.conf.example3 -@@ -27,12 +27,38 @@ - # you can access at http://support.ntp.org/bin/view/Servers/WebHome or +@@ -29,12 +29,38 @@ + # you can access at https://support.ntp.org/bin/view/Servers/WebHome or # you can use servers from the pool.ntp.org project. -! server ntp1.example.net iburst @@ -43,7 +43,7 @@ ####################################################################### ### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK # -@@ -79,7 +105,7 @@ +@@ -81,7 +107,7 @@ # immediately so that it doesn't gain or lose any more time. You # generally want this, so it is uncommented. @@ -52,7 +52,7 @@ # If you want to enable NTP authentication with symmetric keys, you will need # to uncomment the following line and edit the file to set up the keys. -@@ -165,8 +191,8 @@ ntsdumpdir /var/lib/chrony +@@ -167,8 +193,8 @@ ntsdumpdir /var/lib/chrony # produce some graphs of your system's timekeeping performance, or you # need help in debugging a problem. ++++++ chrony-service-helper.patch ++++++ --- /var/tmp/diff_new_pack.H2SM0p/_old 2025-07-10 23:15:11.792909225 +0200 +++ /var/tmp/diff_new_pack.H2SM0p/_new 2025-07-10 23:15:11.796909391 +0200 @@ -1,11 +1,11 @@ --- examples/chronyd.service.orig +++ examples/chronyd.service -@@ -10,6 +10,7 @@ Type=forking +@@ -10,6 +10,7 @@ Type=notify PIDFile=/run/chrony/chronyd.pid + Environment="OPTIONS=" EnvironmentFile=-/etc/sysconfig/chronyd - ExecStart=/usr/sbin/chronyd $OPTIONS +ExecStartPost=@CHRONY_HELPER@ update-daemon + ExecStart=/usr/sbin/chronyd -n $OPTIONS CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE - CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE ++++++ clknetsim-633a0be.tar.gz -> clknetsim-530d1a5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/README new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/README --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/README 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/README 2025-06-24 15:51:35.000000000 +0200 @@ -57,7 +57,9 @@ networks with IPv4 addressing. All nodes have interfaces to all networks. Their addresses are 192.168.122+s.n, where n is the number of the node (starting at 1) and s is the number of the network (starting at 1). The -broadcast addresses are 192.168.122+s.255. +broadcast addresses are 192.168.122+s.255. The CLKNETSIM_IP_FAMILY variable +can be set to 6 to enable IPv6 and disable IPv4. The supported IPv6 addresses +are fc00::123:SSNN, where SS is s-1 in hexadecimal and NN is n in hexadecimal. At the end of the simulation clock and network statistics are printed. clknetsim has options which can be used to control for how long the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/client.c new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/client.c --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/client.c 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/client.c 2025-06-24 15:51:35.000000000 +0200 @@ -77,8 +77,16 @@ #define NODE_FROM_ADDR(addr) (((addr) & ~NETMASK) - 1) #define SUBNET_FROM_ADDR(addr) ((((addr) & NETMASK) - BASE_ADDR) / 0x100) +#define IP6_NET "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x23\x00\x00" +#define IS_SIN6_KNOWN(sin6) (memcmp(IP6_NET, &(sin6)->sin6_addr.s6_addr, 14) == 0) +#define NODE_FROM_SIN6(sin6) ((sin6)->sin6_addr.s6_addr[15]) +#define SUBNET_FROM_SIN6(sin6) ((sin6)->sin6_addr.s6_addr[14]) +#define ADDR_FROM_SIN6(sin6) (BASE_ADDR + (SUBNET_FROM_SIN6(sin6) << 8) + NODE_FROM_SIN6(sin6)) + #define PTP_PRIMARY_MCAST_ADDR 0xe0000181 /* 224.0.1.129 */ #define PTP_PDELAY_MCAST_ADDR 0xe000006b /* 224.0.0.107 */ +#define PTP_PRIMARY_MCAST_ADDR6 "\xff\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x81" +#define PTP_PDELAY_MCAST_ADDR6 "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6b" #define REFCLK_FD 1000 #define REFCLK_ID ((clockid_t)(((unsigned int)~REFCLK_FD << 3) | 3)) @@ -134,15 +142,18 @@ static int initializing = 0; static int initialized_symbols = 0; static int initialized = 0; -static int clknetsim_fd; +static int clknetsim_fd = -1; static int precision_hack = 1; static unsigned int random_seed = 0; +static int ip_family = 4; static int recv_multiply = 1; static int timestamping = 1; static double phc_delay = 0.0; static double phc_jitter = 0.0; static double phc_jitter_asym = 0.0; +static int phc_jitter_off = 0; +static int phc_jitter_on = 1; static int phc_swap = 0; /* Ethernet speed in Mb/s */ @@ -240,6 +251,7 @@ static FILE *pcap = NULL; +static int timer_delete_(timer_t timerid); static void write_pcap_header(void); static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen); @@ -307,6 +319,10 @@ if (env) random_seed = atoi(env); + env = getenv("CLKNETSIM_IP_FAMILY"); + if (env) + ip_family = atoi(env); + env = getenv("CLKNETSIM_RECV_MULTIPLY"); if (env) recv_multiply = atoi(env); @@ -331,6 +347,14 @@ if (env) phc_jitter_asym = atof(env); + env = getenv("CLKNETSIM_PHC_JITTER_OFF"); + if (env) + phc_jitter_off = atoi(env); + + env = getenv("CLKNETSIM_PHC_JITTER_ON"); + if (env) + phc_jitter_on = atoi(env); + env = getenv("CLKNETSIM_PHC_SWAP"); if (env) phc_swap = atoi(env); @@ -418,6 +442,9 @@ if (pcap) fclose(pcap); + + if (clknetsim_fd >= 0) + close(clknetsim_fd); } static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen) { @@ -577,24 +604,103 @@ } } -static void get_target(int socket, uint32_t addr, unsigned int *subnet, unsigned int *node) { - if (addr == PTP_PRIMARY_MCAST_ADDR || addr == PTP_PDELAY_MCAST_ADDR) { - assert(sockets[socket].iface >= IFACE_ETH0); - *subnet = sockets[socket].iface - IFACE_ETH0; - *node = -1; /* multicast as broadcast */ - } else { - *subnet = SUBNET_FROM_ADDR(addr); - if (fuzz_mode && (*subnet >= subnets || *subnet == unix_subnet)) - *subnet = 0; +static int get_ip_target(int socket, const struct sockaddr *saddr, socklen_t saddrlen, + unsigned int *subnet, unsigned int *node, unsigned int *port) { + const struct sockaddr_in6 *sin6; + const struct sockaddr_in *sin; + uint32_t addr; - assert(*subnet >= 0 && *subnet < subnets); - assert(socket_in_subnet(socket, *subnet)); + switch (saddr->sa_family) { + case AF_INET: + sin = (const struct sockaddr_in *)saddr; + if (saddrlen < sizeof (*sin)) + return 0; + addr = ntohl(sin->sin_addr.s_addr); + *port = ntohs(sin->sin_port); + + if (addr == PTP_PRIMARY_MCAST_ADDR || addr == PTP_PDELAY_MCAST_ADDR) { + assert(sockets[socket].iface >= IFACE_ETH0); + *subnet = sockets[socket].iface - IFACE_ETH0; + *node = -1; /* multicast as broadcast */ + return 1; + } + break; + case AF_INET6: + sin6 = (const struct sockaddr_in6 *)saddr; + if (saddrlen < sizeof (*sin6)) + return 0; + *port = ntohs(sin6->sin6_port); + if (memcmp(sin6->sin6_addr.s6_addr, PTP_PRIMARY_MCAST_ADDR6, 16) == 0 || + memcmp(sin6->sin6_addr.s6_addr, PTP_PDELAY_MCAST_ADDR6, 16) == 0) { + assert(sockets[socket].iface >= IFACE_ETH0); + *subnet = sockets[socket].iface - IFACE_ETH0; + *node = -1; + return 1; + } + if (!IS_SIN6_KNOWN(sin6)) + return 0; + addr = ADDR_FROM_SIN6(sin6); + break; + default: + return 0; + } - if (addr == BROADCAST_ADDR(*subnet)) - *node = -1; /* broadcast */ - else - *node = NODE_FROM_ADDR(addr); + *subnet = SUBNET_FROM_ADDR(addr); + if (fuzz_mode && (*subnet >= subnets || *subnet == unix_subnet)) + *subnet = 0; + + assert(*subnet >= 0 && *subnet < subnets); + assert(socket_in_subnet(socket, *subnet)); + + if (addr == BROADCAST_ADDR(*subnet)) + *node = -1; /* broadcast */ + else + *node = NODE_FROM_ADDR(addr); + + return 1; +} + +static int set_sockaddr(int domain, unsigned int subnet, unsigned int node, unsigned int port, + struct sockaddr *saddr, socklen_t *saddrlen) { + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + struct sockaddr_un *sun; + + switch (domain) { + case AF_INET: + assert(*saddrlen >= sizeof (*sin)); + sin = (struct sockaddr_in *)saddr; + memset(sin, 0, sizeof (*sin)); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + sin->sin_addr.s_addr = htonl(NODE_ADDR(subnet, node)); + *saddrlen = sizeof (*sin); + break; + case AF_INET6: + assert(*saddrlen >= sizeof (*sin6)); + sin6 = (struct sockaddr_in6 *)saddr; + memset(sin6, 0, sizeof (*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + memcpy(sin6->sin6_addr.s6_addr, IP6_NET, 14); + sin6->sin6_addr.s6_addr[14] = subnet; + sin6->sin6_addr.s6_addr[15] = node + 1; + *saddrlen = sizeof (*sin6); + break; + case AF_UNIX: + assert(*saddrlen >= sizeof (*sun)); + sun = (struct sockaddr_un *)saddr; + memset(sun, 0, sizeof (*sun)); + sun->sun_family = AF_UNIX; + snprintf(sun->sun_path, sizeof (sun->sun_path), + "/clknetsim/unix/%d:%d", node + 1, port); + *saddrlen = sizeof (*sun); + break; + default: + return 0; } + + return 1; } static int get_network_from_iface(const char *iface) { @@ -676,7 +782,7 @@ static void send_msg_to_peer(int s, int type) { struct Request_send req; - assert(sockets[s].domain == AF_INET); + assert(sockets[s].domain == AF_INET || sockets[s].domain == AF_INET6); assert(sockets[s].type == SOCK_STREAM); if (sockets[s].remote_node == -1) @@ -784,7 +890,7 @@ static void add_to_timespec(struct timespec *tp, double offset) { tp->tv_sec += floor(offset); - tp->tv_nsec += (offset - floor(offset)) * 1e9; + tp->tv_nsec += round((offset - floor(offset)) * 1e9); normalize_timespec(tp); } @@ -793,60 +899,85 @@ } static double get_phc_delay(int dir) { + static unsigned int count = 0; double L, p, delay = 0.0; int k, lambda = 5; /* Poisson with uniform steps */ - if (phc_jitter > 0.0) { + if (phc_jitter > 0.0 && count >= phc_jitter_off) { for (L = exp(-lambda), p = 1.0, k = 0; k < 100 && p > L; k++) p *= get_random_double(); delay += (k + get_random_double()) / (lambda + 0.5) * phc_jitter * (0.5 + dir * phc_jitter_asym); } + count++; + if (count >= phc_jitter_on + phc_jitter_off) + count = 0; + return (delay + phc_delay / 2.0) * (freq_error + 1.0); } static int generate_eth_frame(unsigned int type, unsigned int subnet, unsigned int from, unsigned int to, unsigned int src_port, unsigned int dst_port, char *data, unsigned int data_len, char *frame, unsigned int buf_len) { - uint16_t port1, port2, ip_len, udp_len; + uint16_t port1, port2, ip_len, udp_len, len_offset, proto_offset, ip_header_len; uint32_t addr1, addr2; + ip_header_len = ip_family == 6 ? 40 : 20; + assert(type == SOCK_DGRAM || type == SOCK_STREAM); - if ((type == SOCK_DGRAM && data_len + 42 > buf_len) || - (type == SOCK_STREAM && data_len + 54 > buf_len)) + if ((type == SOCK_DGRAM && data_len + 14 + ip_header_len + 8 > buf_len) || + (type == SOCK_STREAM && data_len + 14 + ip_header_len + 20 > buf_len)) return 0; - addr1 = htonl(NODE_ADDR(subnet, from)); - addr2 = htonl(NODE_ADDR(subnet, to)); + memset(frame, 0, buf_len); + if (ip_family == 6) { + frame[12] = 0x86; + frame[13] = 0xDD; + frame[14] = 0x60; + len_offset = 14 + 4; + ip_len = 0; + proto_offset = 14 + 6; + memcpy(frame + 14 + 8, IP6_NET, 16); + frame[14 + 8 + 14] = subnet; + frame[14 + 8 + 15] = from + 1; + memcpy(frame + 14 + 24, IP6_NET, 16); + frame[14 + 24 + 14] = subnet; + frame[14 + 24 + 15] = to + 1; + } else { + frame[12] = 0x08; + frame[14] = 0x45; + len_offset = 14 + 2; + ip_len = ip_header_len; + proto_offset = 14 + 9; + addr1 = htonl(NODE_ADDR(subnet, from)); + addr2 = htonl(NODE_ADDR(subnet, to)); + memcpy(frame + 14 + 12, &addr1, sizeof (addr1)); + memcpy(frame + 14 + 16, &addr2, sizeof (addr2)); + } + port1 = htons(src_port); port2 = htons(dst_port); - - memset(frame, 0, buf_len); - frame[12] = 0x08; - frame[14] = 0x45; - memcpy(frame + 26, &addr1, sizeof (addr1)); - memcpy(frame + 30, &addr2, sizeof (addr2)); - memcpy(frame + 34, &port1, sizeof (port1)); - memcpy(frame + 36, &port2, sizeof (port2)); + memcpy(frame + 14 + ip_header_len + 0, &port1, sizeof (port1)); + memcpy(frame + 14 + ip_header_len + 2, &port2, sizeof (port2)); if (type == SOCK_DGRAM) { - ip_len = htons(data_len + 28); + ip_len = htons(ip_len + 8 + data_len); udp_len = htons(data_len + 8); - memcpy(frame + 16, &ip_len, sizeof (ip_len)); - frame[23] = 17; - memcpy(frame + 38, &udp_len, sizeof (udp_len)); - memcpy(frame + 42, data, data_len); - return data_len + 42; + memcpy(frame + len_offset, &ip_len, sizeof (ip_len)); + frame[proto_offset] = 17; + memcpy(frame + 14 + ip_header_len + 4, &udp_len, sizeof (udp_len)); + memcpy(frame + 14 + ip_header_len + 8, data, data_len); + return 14 + ip_header_len + 8 + data_len; } else { - ip_len = htons(data_len + 40); - memcpy(frame + 16, &ip_len, sizeof (ip_len)); - frame[23] = 6; - frame[46] = 5 << 4; - memcpy(frame + 54, data, data_len); - return data_len + 54; + ip_len = htons(ip_len + 8 + data_len); + memcpy(frame + len_offset, &ip_len, sizeof (ip_len)); + frame[proto_offset] = 6; + frame[14 + ip_header_len + 12] = 5 << 4; + memcpy(frame + 14 + ip_header_len + 20, data, data_len); + return 14 + ip_header_len + 20 + data_len; } } @@ -972,7 +1103,7 @@ } int clock_settime(clockid_t which_clock, const struct timespec *tp) { - assert(tp && which_clock == CLOCK_REALTIME); + assert(which_clock == CLOCK_REALTIME); if (tp->tv_sec < 0 || tp->tv_sec > ((1LLU << 63) / 1000000000)) { errno = EINVAL; @@ -996,6 +1127,8 @@ req.timex.freq = buf->freq; if (buf->modes & ADJ_MAXERROR) req.timex.maxerror = buf->maxerror; + if (buf->modes & ADJ_ESTERROR) + req.timex.esterror = buf->esterror; if (buf->modes & ADJ_STATUS) req.timex.status = buf->status; if ((buf->modes & ADJ_TIMECONST) || (buf->modes & ADJ_TAI)) @@ -1086,14 +1219,20 @@ int i, timer, s, recv_fd = -1; double elapsed = 0.0; + req.read = 0; + req._pad = 0; + if (writefds) { for (i = 0; i < nfds; i++) { if (!FD_ISSET(i, writefds)) continue; s = get_socket_from_fd(i); - if (s < 0 || - (sockets[s].type == SOCK_STREAM && !sockets[s].connected)) + if (s < 0) + continue; + if (sockets[s].type == SOCK_STREAM && !sockets[s].connected) { + req.read = 1; continue; + } FD_ZERO(writefds); FD_SET(i, writefds); if (exceptfds) @@ -1122,9 +1261,6 @@ FD_ZERO(exceptfds); } - req.read = 0; - req._pad = 0; - /* unknown reading fds are always ready (e.g. chronyd waiting for name resolving notification, or OpenSSL waiting for /dev/urandom) */ @@ -1152,8 +1288,7 @@ timer = get_first_timer(readfds); - assert((timeout && (timeout->tv_sec > 0 || timeout->tv_usec > 0)) || - timer >= 0 || find_recv_socket(NULL) >= 0); + assert(timeout || timer >= 0 || find_recv_socket(NULL) >= 0); fetch_time(); @@ -1296,6 +1431,8 @@ FD_ZERO(&wfds); FD_ZERO(&efds); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" for (i = 0; i < nfds; i++) { if (fds[i].fd < 0) continue; @@ -1309,6 +1446,7 @@ if (maxfd < fds[i].fd) maxfd = fds[i].fd; } +#pragma GCC diagnostic pop if (timeout >= 0) { tv.tv_sec = timeout / 1000; @@ -1412,6 +1550,8 @@ if (fd == URANDOM_FD) return URANDOM_FILE; + init_symbols(); + return _fdopen(fd, mode); } @@ -1493,6 +1633,10 @@ return r; } +int __open_2(const char *pathname, int oflag) { + return open(pathname, oflag); +} + ssize_t read(int fd, void *buf, size_t count) { int t; @@ -1533,6 +1677,10 @@ return _read(fd, buf, count); } +ssize_t __read_chk(int fd, void *buf, size_t count, size_t buflen) { + return read(fd, buf, count); +} + int close(int fd) { int t, s; @@ -1542,7 +1690,7 @@ pps_fds--; return 0; } else if ((t = get_timer_from_fd(fd)) >= 0) { - return timer_delete(get_timerid(t)); + return timer_delete_(get_timerid(t)); } else if ((s = get_socket_from_fd(fd)) >= 0) { if (sockets[s].type == SOCK_STREAM) shutdown(fd, SHUT_RDWR); @@ -1556,7 +1704,9 @@ int socket(int domain, int type, int protocol) { int s; - if ((domain != AF_INET && (domain != AF_UNIX || unix_subnet < 0)) || + if (((domain != AF_INET || ip_family == 6) && + (domain != AF_INET6 || ip_family == 4) && + (domain != AF_UNIX || unix_subnet < 0)) || (type != SOCK_DGRAM && type != SOCK_STREAM)) { errno = EINVAL; return -1; @@ -1583,7 +1733,8 @@ int listen(int sockfd, int backlog) { int s = get_socket_from_fd(sockfd); - if (s < 0 || sockets[s].domain != AF_INET || sockets[s].type != SOCK_STREAM) { + if (s < 0 || (sockets[s].domain != AF_INET && sockets[s].domain != AF_INET6) || + sockets[s].type != SOCK_STREAM) { errno = EINVAL; return -1; } @@ -1595,10 +1746,10 @@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int s = get_socket_from_fd(sockfd), r; - struct sockaddr_in *in; struct Reply_recv rep; - if (s < 0 || sockets[s].domain != AF_INET || sockets[s].type != SOCK_STREAM) { + if (s < 0 || (sockets[s].domain != AF_INET && sockets[s].domain != AF_INET6) || + sockets[s].type != SOCK_STREAM) { errno = EINVAL; return -1; } @@ -1606,7 +1757,7 @@ make_request(REQ_RECV, NULL, 0, &rep, sizeof (rep)); assert(rep.type == MSG_TYPE_TCP_CONNECT); - r = socket(AF_INET, SOCK_STREAM, 0); + r = socket(sockets[s].domain, SOCK_STREAM, 0); s = get_socket_from_fd(r); assert(s >= 0); @@ -1616,12 +1767,8 @@ sockets[s].remote_port = rep.src_port; sockets[s].connected = 1; - in = (struct sockaddr_in *)addr; - assert(*addrlen >= sizeof (*in)); - *addrlen = sizeof (*in); - in->sin_family = AF_INET; - in->sin_port = htons(sockets[s].remote_port); - in->sin_addr.s_addr = htonl(NODE_ADDR(sockets[s].iface - IFACE_ETH0, node)); + set_sockaddr(sockets[s].domain, sockets[s].iface - IFACE_ETH0, node, + sockets[s].remote_port, addr, addrlen); send_msg_to_peer(s, MSG_TYPE_TCP_CONNECT); @@ -1637,7 +1784,7 @@ return -1; } - assert(sockets[s].domain == AF_INET); + assert(sockets[s].domain == AF_INET || sockets[s].domain == AF_INET6); assert(sockets[s].type == SOCK_STREAM); if (sockets[s].connected) { @@ -1652,8 +1799,7 @@ /* ntpd uses connect() and getsockname() to find the interface which will be used to send packets to an address */ int s = get_socket_from_fd(sockfd); - unsigned int node, subnet; - struct sockaddr_in *sin; + unsigned int node, subnet, port; struct sockaddr_un *sun; if (s < 0) { @@ -1663,21 +1809,20 @@ switch (addr->sa_family) { case AF_INET: - sin = (struct sockaddr_in *)addr; - assert(addrlen >= sizeof (*sin)); - get_target(s, ntohl(sin->sin_addr.s_addr), &subnet, &node); - if (node == -1) { + case AF_INET6: + if (!get_ip_target(s, addr, addrlen, &subnet, &node, &port) || + node == -1) { errno = EINVAL; return -1; } sockets[s].iface = IFACE_ETH0 + subnet; sockets[s].remote_node = node; - sockets[s].remote_port = ntohs(sin->sin_port); + sockets[s].remote_port = port; break; case AF_UNIX: sun = (struct sockaddr_un *)addr; - assert(addrlen >= sizeof (*sun)); + assert(addrlen > offsetof(struct sockaddr_un, sun_path) + 1); assert(sockets[s].iface == IFACE_UNIX); if (sscanf(sun->sun_path, "/clknetsim/unix/%d:%d", @@ -1700,8 +1845,8 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int s = get_socket_from_fd(sockfd), port; + struct sockaddr_in6 *sin6; struct sockaddr_in *sin; - struct sockaddr_un *sun; uint32_t a; static int unix_sockets = 0; @@ -1738,9 +1883,28 @@ } } break; + case AF_INET6: + assert(addrlen >= sizeof (*sin6)); + sin6 = (struct sockaddr_in6 *)addr; + + port = ntohs(sin6->sin6_port); + if (port) + sockets[s].port = port; + + if (memcmp(sin6->sin6_addr.s6_addr, in6addr_any.s6_addr, 16) == 0) { + sockets[s].iface = IFACE_ALL; + } else if (memcmp(sin6->sin6_addr.s6_addr, in6addr_loopback.s6_addr, 16) == 0) { + sockets[s].iface = IFACE_LO; + } else { + int subnet = SUBNET_FROM_SIN6(sin6); + assert(IS_SIN6_KNOWN(sin6)); + assert(subnet >= 0 && subnet < subnets); + assert(NODE_FROM_SIN6(sin6) == node); + sockets[s].iface = IFACE_ETH0 + subnet; + } + break; case AF_UNIX: - assert(addrlen >= sizeof (*sun)); - sun = (struct sockaddr_un *)addr; + assert(addrlen > offsetof(struct sockaddr_un, sun_path) + 1); assert(sockets[s].iface == IFACE_UNIX); sockets[s].port = ++unix_sockets; @@ -1757,11 +1921,16 @@ int s = get_socket_from_fd(sockfd); uint32_t a; - if (s < 0 || sockets[s].domain != AF_INET) { + if (s < 0 || (sockets[s].domain != AF_INET && sockets[s].domain != AF_INET6)) { errno = EINVAL; return -1; } + if (sockets[s].domain == AF_INET6) { + return !set_sockaddr(sockets[s].domain, sockets[s].iface - IFACE_ETH0, + node, sockets[s].port, addr, addrlen); + } + struct sockaddr_in *in; in = (struct sockaddr_in *)addr; assert(*addrlen >= sizeof (*in)); @@ -1793,7 +1962,7 @@ int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { int subnet, s = get_socket_from_fd(sockfd); - if (s < 0 || sockets[s].domain != AF_INET) { + if (s < 0 || (sockets[s].domain != AF_INET && sockets[s].domain != AF_INET6)) { errno = EINVAL; return -1; } @@ -1808,7 +1977,8 @@ return -1; } } - else if (level == IPPROTO_IP && optname == IP_PKTINFO && optlen == sizeof (int)) + else if (optlen == sizeof (int) && ((level == IPPROTO_IP && optname == IP_PKTINFO) || + (level == IPPROTO_IPV6 && optname == IPV6_RECVPKTINFO))) sockets[s].pkt_info = !!(int *)optval; #ifdef SO_TIMESTAMPING else if (level == SOL_SOCKET && optname == SO_TIMESTAMPING && optlen >= sizeof (int)) { @@ -1827,7 +1997,7 @@ int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { int s = get_socket_from_fd(sockfd); - if (s < 0 || sockets[s].domain != AF_INET) { + if (s < 0 || (sockets[s].domain != AF_INET && sockets[s].domain != AF_INET6)) { errno = EINVAL; return -1; } @@ -2048,24 +2218,24 @@ } else if (request == PTP_SYS_OFFSET_EXTENDED && fd == REFCLK_FD) { struct ptp_sys_offset_extended *sys_off = va_arg(ap, struct ptp_sys_offset_extended *); struct timespec ts, ts1, ts2; + double delay; int i; if (sys_off->n_samples > PTP_MAX_SAMPLES) sys_off->n_samples = PTP_MAX_SAMPLES; for (i = 0; i < sys_off->n_samples; i++) { + clock_gettime(CLOCK_REALTIME, &ts2); clock_gettime(REFCLK_ID, &ts); - sys_off->ts[i][1].sec = ts.tv_sec; - sys_off->ts[i][1].nsec = ts.tv_nsec; - } - - clock_gettime(CLOCK_REALTIME, &ts); - for (i = 0; i < sys_off->n_samples; i++) { - ts1 = ts, ts2 = ts; - add_to_timespec(&ts1, -get_phc_delay(-1)); - add_to_timespec(&ts2, get_phc_delay(1)); + delay = get_phc_delay(1); + add_to_timespec(&ts, -delay); + delay += get_phc_delay(-1); + ts1 = ts2; + add_to_timespec(&ts1, -delay); sys_off->ts[i][0].sec = ts1.tv_sec; sys_off->ts[i][0].nsec = ts1.tv_nsec; + sys_off->ts[i][1].sec = ts.tv_sec; + sys_off->ts[i][1].nsec = ts.tv_nsec; sys_off->ts[i][2].sec = ts2.tv_sec; sys_off->ts[i][2].nsec = ts2.tv_nsec; } @@ -2239,7 +2409,6 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { struct Request_send req; - struct sockaddr_in *sin; struct sockaddr_un *sun; struct cmsghdr *cmsg; int i, s = get_socket_from_fd(sockfd), timestamping; @@ -2262,15 +2431,16 @@ } else { switch (sockets[s].domain) { case AF_INET: - sin = msg->msg_name; - assert(sin && msg->msg_namelen >= sizeof (*sin)); - assert(sin->sin_family == AF_INET); - get_target(s, ntohl(sin->sin_addr.s_addr), &req.subnet, &req.to); - req.dst_port = ntohs(sin->sin_port); + case AF_INET6: + if (!get_ip_target(s, msg->msg_name, msg->msg_namelen, &req.subnet, + &req.to, &req.dst_port)) { + errno = EINVAL; + return -1; + } break; case AF_UNIX: sun = msg->msg_name; - assert(sun && msg->msg_namelen >= sizeof (*sun)); + assert(sun && msg->msg_namelen > offsetof(struct sockaddr_un, sun_path) + 1); assert(sun->sun_family == AF_UNIX); req.subnet = unix_subnet; if (sscanf(sun->sun_path, "/clknetsim/unix/%u:%u", @@ -2398,8 +2568,6 @@ ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { struct message *last_ts_msg = NULL; struct Reply_recv rep; - struct sockaddr_in *sin; - struct sockaddr_un *sun; struct cmsghdr *cmsg; int msglen, cmsglen, s = get_socket_from_fd(sockfd); @@ -2428,7 +2596,6 @@ sockets[s].port, last_ts_msg->port, last_ts_msg->data, last_ts_msg->len, rep.data, sizeof (rep.data)); - rep.len = 42 + last_ts_msg->len; last_ts_msg->len = 0; } else if (sockets[s].buffer.len > 0) { @@ -2492,25 +2659,8 @@ assert(sockets[s].remote_port == -1 || sockets[s].remote_port == rep.src_port); if (msg->msg_name) { - switch (sockets[s].domain) { - case AF_INET: - assert(msg->msg_namelen >= sizeof (*sin)); - sin = msg->msg_name; - sin->sin_family = AF_INET; - sin->sin_port = htons(rep.src_port); - sin->sin_addr.s_addr = htonl(NODE_ADDR(rep.subnet, rep.from)); - msg->msg_namelen = sizeof (struct sockaddr_in); - break; - case AF_UNIX: - assert(msg->msg_namelen >= sizeof (*sun)); - sun = msg->msg_name; - sun->sun_family = AF_UNIX; - snprintf(sun->sun_path, sizeof (sun->sun_path), - "/clknetsim/unix/%d:%d", rep.from + 1, rep.src_port); - break; - default: - assert(0); - } + set_sockaddr(sockets[s].domain, rep.subnet, rep.from, rep.src_port, + msg->msg_name, &msg->msg_namelen); } assert(msg->msg_iovlen == 1); @@ -2532,7 +2682,7 @@ cmsglen = 0; - if (sockets[s].pkt_info) { + if (sockets[s].pkt_info && sockets[s].domain == AF_INET) { struct in_pktinfo ipi; cmsglen = CMSG_SPACE(sizeof (ipi)); @@ -2550,6 +2700,25 @@ ipi.ipi_ifindex = rep.subnet + 1; memcpy(CMSG_DATA(cmsg), &ipi, sizeof (ipi)); + } else if (sockets[s].pkt_info && sockets[s].domain == AF_INET6) { + struct in6_pktinfo ipi; + + cmsglen = CMSG_SPACE(sizeof (ipi)); + assert(msg->msg_control && msg->msg_controllen >= cmsglen); + + cmsg = CMSG_FIRSTHDR(msg); + memset(cmsg, 0, sizeof (*cmsg)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof (ipi)); + + memset(&ipi, 0, sizeof (ipi)); + memcpy(ipi.ipi6_addr.s6_addr, IP6_NET, 14); + ipi.ipi6_addr.s6_addr[14] = rep.subnet; + ipi.ipi6_addr.s6_addr[15] = node + 1; + ipi.ipi6_ifindex = rep.subnet + 1; + + memcpy(CMSG_DATA(cmsg), &ipi, sizeof (ipi)); } #ifdef SO_TIMESTAMPING @@ -2646,7 +2815,7 @@ return recv(fd, buf, len, flags); } -int timer_create(clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id) { +static int timer_create_(clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id) { int t; assert(which_clock == CLOCK_REALTIME && timer_event_spec == NULL); @@ -2669,7 +2838,11 @@ return 0; } -int timer_delete(timer_t timerid) { +int timer_create(clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id) { + return timer_create_(which_clock, timer_event_spec, created_timer_id); +} + +static int timer_delete_(timer_t timerid) { int t = get_timer_from_id(timerid); if (t < 0) { @@ -2682,7 +2855,11 @@ return 0; } -int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { +int timer_delete(timer_t timerid) { + return timer_delete_(timerid); +} + +static int timer_settime_(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { int t = get_timer_from_id(timerid); if (t < 0) { @@ -2707,7 +2884,11 @@ return 0; } -int timer_gettime(timer_t timerid, struct itimerspec *value) { +int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { + return timer_settime_(timerid, flags, value, ovalue); +} + +int timer_gettime_(timer_t timerid, struct itimerspec *value) { double timeout; int t = get_timer_from_id(timerid); @@ -2728,6 +2909,10 @@ return 0; } +int timer_gettime(timer_t timerid, struct itimerspec *value) { + return timer_gettime_(timerid, value); +} + #ifndef CLKNETSIM_DISABLE_ITIMER int setitimer(__itimer_which_t which, const struct itimerval *new_value, struct itimerval *old_value) { struct itimerspec timerspec; @@ -2790,11 +2975,11 @@ else assert(!flags); - return timer_settime(get_timerid(get_timer_from_fd(fd)), flags, new_value, old_value); + return timer_settime_(get_timerid(get_timer_from_fd(fd)), flags, new_value, old_value); } int timerfd_gettime(int fd, struct itimerspec *curr_value) { - return timer_gettime(get_timerid(get_timer_from_fd(fd)), curr_value); + return timer_gettime_(get_timerid(get_timer_from_fd(fd)), curr_value); } int shmget(key_t key, size_t size, int shmflg) { @@ -2986,22 +3171,34 @@ return 0; } -static struct addrinfo *get_addrinfo(uint32_t addr, int port, int type, struct addrinfo *next) { +static struct addrinfo *get_addrinfo(int family, uint32_t addr, int port, int type, struct addrinfo *next) { + struct sockaddr_in6 *sin6; struct sockaddr_in *sin; struct addrinfo *r; - - sin = malloc(sizeof *sin); - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - sin->sin_addr.s_addr = htonl(addr); + socklen_t len; r = malloc(sizeof *r); memset(r, 0, sizeof *r); - r->ai_family = AF_INET; - r->ai_socktype = type; - r->ai_addrlen = sizeof *sin; - r->ai_addr = (struct sockaddr *)sin; + if (family == 6) { + sin6 = malloc(sizeof *sin6); + len = sizeof (*sin6); + set_sockaddr(AF_INET6, SUBNET_FROM_ADDR(addr), NODE_FROM_ADDR(addr), port, + (struct sockaddr *)sin6, &len); + r->ai_family = AF_INET6; + r->ai_socktype = type; + r->ai_addrlen = sizeof *sin6; + r->ai_addr = (struct sockaddr *)sin6; + } else { + sin = malloc(sizeof *sin); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + sin->sin_addr.s_addr = htonl(addr); + r->ai_family = AF_INET; + r->ai_socktype = type; + r->ai_addrlen = sizeof *sin; + r->ai_addr = (struct sockaddr *)sin; + } r->ai_next = next; @@ -3010,14 +3207,21 @@ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { - int port = 0, type = SOCK_DGRAM; + int family = ip_family, port = 0, type = SOCK_DGRAM; struct in_addr addr; if (hints) { - if ((hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET) || - (hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM && - hints->ai_socktype != 0)) + if (hints->ai_family == AF_INET) + family = 4; + else if (hints->ai_family == AF_INET6) + family = 6; + else if (hints->ai_family != AF_UNSPEC) + return EAI_NONAME; + + if (hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM && + hints->ai_socktype != 0) return EAI_NONAME; + if (hints->ai_socktype == SOCK_STREAM) type = SOCK_STREAM; } @@ -3032,9 +3236,10 @@ } if (node == NULL) { - *res = get_addrinfo(INADDR_ANY, port, type, NULL); + assert(ip_family != 6); + *res = get_addrinfo(4, INADDR_ANY, port, type, NULL); } else if (inet_aton(node, &addr)) { - *res = get_addrinfo(ntohl(addr.s_addr), port, type, NULL); + *res = get_addrinfo(4, ntohl(addr.s_addr), port, type, NULL); } else if ((strlen(node) > 4 && strcmp(node + strlen(node) - 4, ".clk") == 0) || (strlen(node) > 5 && strcmp(node + strlen(node) - 5, ".clk.") == 0)) { const char *s = strstr(node, ".net"); @@ -3049,11 +3254,13 @@ s = node + 5; *res = NULL; do { - *res = get_addrinfo(NODE_ADDR(subnet, atoi(s + 1) - 1), port, type, *res); + *res = get_addrinfo(family, NODE_ADDR(subnet, atoi(s + 1) - 1), + port, type, *res); s = strchr(s + 1, '-'); } while (s); } else if (strncmp(node, "node", 4) == 0) { - *res = get_addrinfo(NODE_ADDR(subnet, atoi(node + 4) - 1), port, type, NULL); + *res = get_addrinfo(family, NODE_ADDR(subnet, atoi(node + 4) - 1), + port, type, NULL); } else { return EAI_NONAME; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/clknetsim.bash new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/clknetsim.bash --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/clknetsim.bash 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/clknetsim.bash 2025-06-24 15:51:35.000000000 +0200 @@ -94,9 +94,10 @@ ;; esac + unset LISTEN_FDS NOTIFY_SOCKET + if [[ $CLKNETSIM_CLIENT_WRAPPER == *valgrind* ]]; then - unset DEBUGINFOD_URLS - wrapper_options="--log-file=$CLKNETSIM_TMPDIR/valgrind.$node" + wrapper_options="--log-file=$CLKNETSIM_TMPDIR/valgrind.$node --enable-debuginfod=no" fi LD_PRELOAD=${CLKNETSIM_PRELOAD:+$CLKNETSIM_PRELOAD:}$CLKNETSIM_PATH/clknetsim.so \ @@ -111,11 +112,11 @@ } start_server() { - local nodes=$1 ret=0 wrapper_options="" + local nodes=$1 ret=0 wrapper_options="" i j shift if [[ $CLKNETSIM_SERVER_WRAPPER == *valgrind* ]]; then - wrapper_options="--log-file=$CLKNETSIM_TMPDIR/valgrind.0" + wrapper_options="--log-file=$CLKNETSIM_TMPDIR/valgrind.0 --enable-debuginfod=no" fi $CLKNETSIM_SERVER_WRAPPER $wrapper_options \ @@ -128,14 +129,30 @@ fi kill $client_pids &> /dev/null + + i=0 + for pid in $client_pids; do + i=$[i + 1] + j=0 + while kill -0 $pid &> /dev/null; do + j=$[j + 1] + if [ $j -gt 30 ]; then + echo " node $i did not terminate" 1>&2 + ret=1 + break + fi + sleep 0.1 + done + done + client_pids=" " - if ls tmp/valgrind.* &> /dev/null; then - if grep -q 'ERROR SUMMARY: [^0]' tmp/valgrind.*; then + if ls $CLKNETSIM_TMPDIR/valgrind.* &> /dev/null; then + if grep -q 'ERROR SUMMARY: [^0]' $CLKNETSIM_TMPDIR/valgrind.*; then echo " valgrind error" 1>&2 ret=1 fi - sed -i '/^ERROR: ld.so: object.*from LD_PRELOAD cannot/d' tmp/log.[0-9]* + sed -i '/^ERROR: ld.so: object.*from LD_PRELOAD cannot/d' $CLKNETSIM_TMPDIR/log.[0-9]* fi return $ret diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/clock.cc new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/clock.cc --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/clock.cc 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/clock.cc 2025-06-24 15:51:35.000000000 +0200 @@ -21,6 +21,7 @@ #define MAXSEC 2048 #define MAXTIMECONST 10 #define MAXMAXERROR 16000000 +#define MAXERROR_RATE 500 #define SHIFT_FLL 2 #define SCALE_FREQ 65536.0e6 #define MAXFREQ_SCALED 32768000 @@ -47,6 +48,9 @@ ntp_timex.tick = base_tick; ntp_timex.tolerance = MAXFREQ_SCALED; ntp_timex.precision = 1; + ntp_timex.maxerror = MAXMAXERROR; + ntp_timex.esterror = MAXMAXERROR; + ntp_timex.status = STA_UNSYNC; ntp_state = TIME_OK; @@ -194,6 +198,12 @@ } else ss_slew = 0; + ntp_timex.maxerror += MAXERROR_RATE; + if (ntp_timex.maxerror >= MAXMAXERROR) { + ntp_timex.maxerror = MAXMAXERROR; + ntp_timex.status |= STA_UNSYNC; + } + switch (ntp_state) { case TIME_OK: if (ntp_timex.status & STA_INS) @@ -293,8 +303,20 @@ else if (ntp_timex.freq < -MAXFREQ_SCALED) ntp_timex.freq = -MAXFREQ_SCALED; } - if (buf->modes & ADJ_MAXERROR) + if (buf->modes & ADJ_MAXERROR) { ntp_timex.maxerror = buf->maxerror; + if (ntp_timex.maxerror > MAXMAXERROR) + ntp_timex.maxerror = MAXMAXERROR; + if (ntp_timex.maxerror < 0) + ntp_timex.maxerror = 0; + } + if (buf->modes & ADJ_ESTERROR) { + ntp_timex.esterror = buf->esterror; + if (ntp_timex.esterror > MAXMAXERROR) + ntp_timex.esterror = MAXMAXERROR; + if (ntp_timex.esterror < 0) + ntp_timex.esterror = 0; + } if (buf->modes & ADJ_STATUS) { if ((buf->status & STA_PLL) && !(ntp_timex.status & STA_PLL)) ntp_update_interval = 0; @@ -336,6 +358,7 @@ } t = ntp_timex; + t.modes = buf->modes; if ((buf->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) { if ((buf->modes & ADJ_OFFSET_SS_READ) == ADJ_OFFSET_SINGLESHOT) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/network.cc new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/network.cc --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/network.cc 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/network.cc 2025-06-24 15:51:35.000000000 +0200 @@ -56,10 +56,12 @@ return 1e20; } -Network::Network(const char *socket, unsigned int n, unsigned int subnets, unsigned int rate) { +Network::Network(const char *socket, const char *executable, + unsigned int n, unsigned int subnets, unsigned int rate) { time = 0.0; this->subnets = subnets; socket_name = socket; + update_executable = executable; update_rate = rate; update_count = 0; offset_log = NULL; @@ -282,6 +284,19 @@ } update_clock_stats(); + + if (update_executable) { + pid_t pid = fork(); + char buf[16]; + + if (pid == 0) { + snprintf(buf, sizeof (buf), "%g", time); + execl(update_executable, update_executable, buf, (char *)NULL); + exit(1); + } else if (pid > 0) { + waitpid(pid, NULL, 0); + } + } } void Network::update_clock_stats() { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/network.h new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/network.h --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/network.h 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/network.h 2025-06-24 15:51:35.000000000 +0200 @@ -57,6 +57,7 @@ unsigned int update_count; const char *socket_name; + const char *update_executable; vector<Node *> nodes; vector<Generator *> link_delays; vector<Generator *> link_corrections; @@ -77,7 +78,7 @@ void write_correction(struct Packet *packet, double correction); public: - Network(const char *socket, unsigned int n, unsigned int s, unsigned int rate); + Network(const char *socket, const char *executable, unsigned int n, unsigned int s, unsigned int rate); ~Network(); bool prepare_clients(); Node *get_node(unsigned int node); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/server.cc new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/server.cc --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/server.cc 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/server.cc 2025-06-24 15:51:35.000000000 +0200 @@ -119,13 +119,13 @@ int nodes, subnets = 1, help = 0, verbosity = 2, generate_only = 0, rate = 1; double limit = 10000.0, reset = 0.0; const char *offset_log = NULL, *freq_log = NULL, *rawfreq_log = NULL, - *packet_log = NULL, *config, *socket = "clknetsim.sock", *env; + *packet_log = NULL, *config, *socket = "clknetsim.sock", *env, *executable = NULL; struct timeval tv; int r, opt; Network *network; - while ((opt = getopt(argc, argv, "l:r:R:n:o:f:Gg:p:s:v:h")) != -1) { + while ((opt = getopt(argc, argv, "l:r:R:n:o:f:Gg:p:s:v:e:h")) != -1) { switch (opt) { case 'l': limit = atof(optarg); @@ -160,6 +160,9 @@ case 'v': verbosity = atoi(optarg); break; + case 'e': + executable = optarg; + break; case 'h': default: help = 1; @@ -179,6 +182,7 @@ printf(" -p file log packet delays to file\n"); printf(" -s socket set server socket name (default clknetsim.sock)\n"); printf(" -v level set verbosity level (default 2)\n"); + printf(" -e file execute file on every freq/log/stats update\n"); printf(" -G print num numbers generated by expr\n"); printf(" -h print usage\n"); return 1; @@ -200,7 +204,7 @@ return 0; } - network = new Network(socket, nodes, subnets, rate); + network = new Network(socket, executable, nodes, subnets, rate); if (offset_log) network->open_offset_log(offset_log); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/sysheaders.h new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/sysheaders.h --- old/clknetsim-633a0be069bac00e5aa562c0e5e15a3bf30b6af2/sysheaders.h 2024-06-10 14:23:41.000000000 +0200 +++ new/clknetsim-530d1a579a8ae1a0bb0f26079d8eef32136699e2/sysheaders.h 2025-06-24 15:51:35.000000000 +0200 @@ -17,6 +17,8 @@ #include <math.h> #include <stddef.h> #include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> #ifdef __linux__ #ifndef ADJ_SETOFFSET