Blame SOURCES/dnsmasq-2.84-bind-dynamic-netlink.patch

9f623f
From 5010c42c47b7b5a3d68d83369d6c17ed0bc11cff Mon Sep 17 00:00:00 2001
9f623f
From: Petr Mensik <pemensik@redhat.com>
9f623f
Date: Wed, 17 Feb 2021 11:47:28 +0100
9f623f
Subject: [PATCH] Correct occasional --bind-dynamic synchronization break
9f623f
9f623f
Request only one re-read of addresses and/or routes
9f623f
9f623f
Previous implementation re-reads systemd addresses exactly the same
9f623f
number of time equal number of notifications received.
9f623f
This is not necessary, we need just notification of change, then re-read
9f623f
the current state and adapt listeners. Repeated re-reading slows netlink
9f623f
processing and highers CPU usage on mass interface changes.
9f623f
9f623f
Continue reading multicast events from netlink, even when ENOBUFS
9f623f
arrive. Broadcasts are not trusted anyway and refresh would be done in
9f623f
iface_enumerate. Save queued events sent again.
9f623f
9f623f
Remove sleeping on netlink ENOBUFS
9f623f
9f623f
With reduced number of written events netlink should receive ENOBUFS
9f623f
rarely. It does not make sense to wait if it is received. It is just a
9f623f
signal some packets got missing. Fast reading all pending packets is required,
9f623f
seq checking ensures it already. Finishes changes by
9f623f
commit 1d07667ac77c55b9de56b1b2c385167e0e0ec27a.
9f623f
9f623f
Move restart from iface_enumerate to enumerate_interfaces
9f623f
9f623f
When ENOBUFS is received, restart of reading addresses is done. But
9f623f
previously found addresses might not have been found this time. In order
9f623f
to catch this, restart both IPv4 and IPv6 enumeration with clearing
9f623f
found interfaces first. It should deliver up-to-date state also after
9f623f
ENOBUFS.
9f623f
9f623f
Read all netlink messages before netlink restart
9f623f
9f623f
Before writing again into netlink socket, try fetching all pending
9f623f
messages. They would be ignored, only might trigger new address
9f623f
synchronization. Should ensure new try has better chance to succeed.
9f623f
9f623f
Request sending ENOBUFS again
9f623f
9f623f
ENOBUFS error handling was improved. Netlink is correctly drained before
9f623f
sending a new request again. It seems ENOBUFS supression is no longer
9f623f
necessary or wanted. Let kernel tell us when it failed and handle it a
9f623f
good way.
9f623f
---
9f623f
 src/netlink.c | 67 ++++++++++++++++++++++++++++++++++++---------------
9f623f
 src/network.c | 11 +++++++--
9f623f
 2 files changed, 57 insertions(+), 21 deletions(-)
9f623f
9f623f
diff --git a/src/netlink.c b/src/netlink.c
9f623f
index ac1a1c5..f95f3e8 100644
9f623f
--- a/src/netlink.c
9f623f
+++ b/src/netlink.c
9f623f
@@ -32,13 +32,21 @@
9f623f
 
9f623f
 #ifndef NDA_RTA
9f623f
 #  define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) 
9f623f
-#endif 
9f623f
+#endif
9f623f
+
9f623f
+/* Used to request refresh of addresses or routes just once,
9f623f
+ * when multiple changes might be announced. */
9f623f
+enum async_states {
9f623f
+  STATE_NEWADDR = (1 << 0),
9f623f
+  STATE_NEWROUTE = (1 << 1),
9f623f
+};
9f623f
 
9f623f
 
9f623f
 static struct iovec iov;
9f623f
 static u32 netlink_pid;
9f623f
 
9f623f
-static void nl_async(struct nlmsghdr *h);
9f623f
+static unsigned nl_async(struct nlmsghdr *h, unsigned state);
9f623f
+static void nl_multicast_state(unsigned state);
9f623f
 
9f623f
 void netlink_init(void)
9f623f
 {
9f623f
@@ -135,7 +143,9 @@ static ssize_t netlink_recv(void)
9f623f
   
9f623f
 
9f623f
 /* family = AF_UNSPEC finds ARP table entries.
9f623f
-   family = AF_LOCAL finds MAC addresses. */
9f623f
+   family = AF_LOCAL finds MAC addresses.
9f623f
+   returns 0 on failure, 1 on success, -1 when restart is required
9f623f
+*/
9f623f
 int iface_enumerate(int family, void *parm, int (*callback)())
9f623f
 {
9f623f
   struct sockaddr_nl addr;
9f623f
@@ -143,6 +153,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
9f623f
   ssize_t len;
9f623f
   static unsigned int seq = 0;
9f623f
   int callback_ok = 1;
9f623f
+  unsigned state = 0;
9f623f
 
9f623f
   struct {
9f623f
     struct nlmsghdr nlh;
9f623f
@@ -154,7 +165,6 @@ int iface_enumerate(int family, void *parm, int (*callback)())
9f623f
   addr.nl_groups = 0;
9f623f
   addr.nl_pid = 0; /* address to kernel */
9f623f
  
9f623f
- again: 
9f623f
   if (family == AF_UNSPEC)
9f623f
     req.nlh.nlmsg_type = RTM_GETNEIGH;
9f623f
   else if (family == AF_LOCAL)
9f623f
@@ -181,8 +191,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
9f623f
 	{
9f623f
 	  if (errno == ENOBUFS)
9f623f
 	    {
9f623f
-	      sleep(1);
9f623f
-	      goto again;
9f623f
+	      nl_multicast_state(state);
9f623f
+	      return -1;
9f623f
 	    }
9f623f
 	  return 0;
9f623f
 	}
9f623f
@@ -191,7 +201,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
9f623f
 	if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
9f623f
 	  {
9f623f
 	    /* May be multicast arriving async */
9f623f
-	    nl_async(h);
9f623f
+	    state = nl_async(h, state);
9f623f
 	  }
9f623f
 	else if (h->nlmsg_seq != seq)
9f623f
 	  {
9f623f
@@ -327,26 +337,36 @@ int iface_enumerate(int family, void *parm, int (*callback)())
9f623f
     }
9f623f
 }
9f623f
 
9f623f
-void netlink_multicast(void)
9f623f
+static void nl_multicast_state(unsigned state)
9f623f
 {
9f623f
   ssize_t len;
9f623f
   struct nlmsghdr *h;
9f623f
   int flags;
9f623f
-  
9f623f
-  /* don't risk blocking reading netlink messages here. */
9f623f
+
9f623f
   if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
9f623f
       fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) 
9f623f
     return;
9f623f
+
9f623f
+  do {
9f623f
+    /* don't risk blocking reading netlink messages here. */
9f623f
+    while ((len = netlink_recv()) != -1)
9f623f
   
9f623f
-  if ((len = netlink_recv()) != -1)
9f623f
-    for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
9f623f
-      nl_async(h);
9f623f
-  
9f623f
+      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
9f623f
+	state = nl_async(h, state);
9f623f
+  } while (errno == ENOBUFS);
9f623f
+
9f623f
   /* restore non-blocking status */
9f623f
   fcntl(daemon->netlinkfd, F_SETFL, flags);
9f623f
 }
9f623f
 
9f623f
-static void nl_async(struct nlmsghdr *h)
9f623f
+void netlink_multicast(void)
9f623f
+{
9f623f
+  unsigned state = 0;
9f623f
+  nl_multicast_state(state);
9f623f
+}
9f623f
+
9f623f
+
9f623f
+static unsigned nl_async(struct nlmsghdr *h, unsigned state)
9f623f
 {
9f623f
   if (h->nlmsg_type == NLMSG_ERROR)
9f623f
     {
9f623f
@@ -354,7 +374,8 @@ static void nl_async(struct nlmsghdr *h)
9f623f
       if (err->error != 0)
9f623f
 	my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
9f623f
     }
9f623f
-  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
9f623f
+  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE &&
9f623f
+	   (state & STATE_NEWROUTE)==0)
9f623f
     {
9f623f
       /* We arrange to receive netlink multicast messages whenever the network route is added.
9f623f
 	 If this happens and we still have a DNS packet in the buffer, we re-send it.
9f623f
@@ -366,10 +387,18 @@ static void nl_async(struct nlmsghdr *h)
9f623f
       if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK &&
9f623f
 	  (rtm->rtm_table == RT_TABLE_MAIN ||
9f623f
 	   rtm->rtm_table == RT_TABLE_LOCAL))
9f623f
-	queue_event(EVENT_NEWROUTE);
9f623f
+	{
9f623f
+	  queue_event(EVENT_NEWROUTE);
9f623f
+	  state |= STATE_NEWROUTE;
9f623f
+	}
9f623f
+    }
9f623f
+  else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) &&
9f623f
+	   (state & STATE_NEWADDR)==0)
9f623f
+    {
9f623f
+      queue_event(EVENT_NEWADDR);
9f623f
+      state |= STATE_NEWADDR;
9f623f
     }
9f623f
-  else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) 
9f623f
-    queue_event(EVENT_NEWADDR);
9f623f
+  return state;
9f623f
 }
9f623f
 #endif
9f623f
 
9f623f
diff --git a/src/network.c b/src/network.c
9f623f
index c6e7d89..47caf38 100644
9f623f
--- a/src/network.c
9f623f
+++ b/src/network.c
9f623f
@@ -656,7 +656,8 @@ int enumerate_interfaces(int reset)
9f623f
 
9f623f
   if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
9f623f
     return 0;
9f623f
- 
9f623f
+
9f623f
+again:
9f623f
   /* Mark interfaces for garbage collection */
9f623f
   for (iface = daemon->interfaces; iface; iface = iface->next) 
9f623f
     iface->found = 0;
9f623f
@@ -709,10 +710,16 @@ int enumerate_interfaces(int reset)
9f623f
   
9f623f
 #ifdef HAVE_IPV6
9f623f
   ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
9f623f
+  if (ret < 0)
9f623f
+    goto again;
9f623f
 #endif
9f623f
 
9f623f
   if (ret)
9f623f
-    ret = iface_enumerate(AF_INET, &param, iface_allowed_v4); 
9f623f
+    {
9f623f
+      ret = iface_enumerate(AF_INET, &param, iface_allowed_v4);
9f623f
+      if (ret < 0)
9f623f
+	goto again;
9f623f
+    }
9f623f
  
9f623f
   errsave = errno;
9f623f
   close(param.fd);
9f623f
-- 
9f623f
2.26.2
9f623f