|
|
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, ¶m, iface_allowed_v6);
|
|
|
7bb5d0 |
+ if (ret < 0)
|
|
|
7bb5d0 |
+ goto again;
|
|
|
7bb5d0 |
#endif
|
|
|
7bb5d0 |
|
|
|
7bb5d0 |
if (ret)
|
|
|
7bb5d0 |
- ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4);
|
|
|
7bb5d0 |
+ {
|
|
|
7bb5d0 |
+ ret = iface_enumerate(AF_INET, ¶m, 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 |
|