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

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