]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
ntp: keep kernel RX timestamping permanently enabled on Linux
authorMiroslav Lichvar <mlichvar@redhat.com>
Wed, 14 Feb 2018 09:11:19 +0000 (10:11 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Fri, 16 Feb 2018 10:09:54 +0000 (11:09 +0100)
The Linux kernel has a counter for sockets using kernel RX timestamping
and timestamps (all) received packets only when it is not zero. However,
this counter is updated asynchronously from setsockopt(). If there are
currently no other sockets using the timestamping, it is possible that a
fast server response is received before the kernel timestamping is
actually enabled after setting the socket option and sending a request.

Open a dummy socket on start to make sure there is always at least one
timestamping socket to avoid the race condition.

ntp_io_linux.c

index 66c2c1e46b586559d2b5208af5d679757021eaf6..eb437e37064f15c29546e58e1a4d0020f72a54a2 100644 (file)
@@ -107,6 +107,12 @@ static SCH_TimeoutID resume_timeout_id;
 
 #define RESUME_TIMEOUT 200.0e-6
 
+/* Unbound socket keeping the kernel RX timestamping permanently enabled
+   in order to avoid a race condition between receiving a server response
+   and the kernel actually starting to timestamp received packets after
+   enabling the timestamping and sending a request */
+static int dummy_rxts_socket;
+
 #define INVALID_SOCK_FD -3
 
 /* ================================================== */
@@ -316,6 +322,29 @@ check_timestamping_option(int option)
 
 /* ================================================== */
 
+static int
+open_dummy_socket(void)
+{
+  int sock_fd, events = 0;
+
+  if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+#ifdef FEAT_IPV6
+      && (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
+#endif
+     )
+    return INVALID_SOCK_FD;
+
+  if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
+    close(sock_fd);
+    return INVALID_SOCK_FD;
+  }
+
+  UTI_FdSetCloexec(sock_fd);
+  return sock_fd;
+}
+
+/* ================================================== */
+
 void
 NIO_Linux_Initialise(void)
 {
@@ -368,6 +397,9 @@ NIO_Linux_Initialise(void)
 
   monitored_socket = INVALID_SOCK_FD;
   suspended_socket = INVALID_SOCK_FD;
+
+  /* Open a socket to keep the kernel RX timestamping permanently enabled */
+  dummy_rxts_socket = open_dummy_socket();
 }
 
 /* ================================================== */
@@ -378,6 +410,9 @@ NIO_Linux_Finalise(void)
   struct Interface *iface;
   unsigned int i;
 
+  if (dummy_rxts_socket != INVALID_SOCK_FD)
+    close(dummy_rxts_socket);
+
   for (i = 0; i < ARR_GetSize(interfaces); i++) {
     iface = ARR_GetElement(interfaces, i);
     HCL_DestroyInstance(iface->clock);