diff --git a/SOURCES/dnsmasq-2.77-netlink-loop.patch b/SOURCES/dnsmasq-2.77-netlink-loop.patch new file mode 100644 index 0000000..b3838c2 --- /dev/null +++ b/SOURCES/dnsmasq-2.77-netlink-loop.patch @@ -0,0 +1,39 @@ +From 5fdb358edc57f41292680a80e9f80f9c2c7f9742 Mon Sep 17 00:00:00 2001 +From: Ivan Kokshaysky +Date: Mon, 11 Jul 2016 18:36:05 +0100 +Subject: [PATCH] Fix logic error in Linux netlink code. + +This could cause dnsmasq to enter a tight loop on systems +with a very large number of network interfaces. + +(cherry picked from commit 1d07667ac77c55b9de56b1b2c385167e0e0ec27a) +--- + src/netlink.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/netlink.c b/src/netlink.c +index 049247b..8cd51af 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -188,11 +188,17 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + } + + for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) +- if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) ++ if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) + { + /* May be multicast arriving async */ + nl_async(h); + } ++ else if (h->nlmsg_seq != seq) ++ { ++ /* May be part of incomplete response to previous request after ++ ENOBUFS. Drop it. */ ++ continue; ++ } + else if (h->nlmsg_type == NLMSG_DONE) + return callback_ok; + else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) +-- +2.26.2 + diff --git a/SOURCES/dnsmasq-2.81-netlink-table.patch b/SOURCES/dnsmasq-2.81-netlink-table.patch new file mode 100644 index 0000000..05d22cf --- /dev/null +++ b/SOURCES/dnsmasq-2.81-netlink-table.patch @@ -0,0 +1,45 @@ +From 595b2e2e87f152c4ade7e2d66cb78915096f60c2 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Mon, 2 Mar 2020 11:23:36 -0500 +Subject: [PATCH] Ignore routes in non-main tables + +Route lookup in Linux is bounded by `ip rules` as well +as the contents of specific routing tables. With the +advent of vrf's(l3mdev's) non-default tables are regularly being +used for routing purposes. + +dnsmasq listens to all route changes on the box and responds +to each one with an event. This is *expensive* when a full +BGP routing table is placed into the linux kernel, moreso +when dnsmasq is responding to events in tables that it will +never actually need to respond to, since dnsmasq at this +point in time has no concept of vrf's and would need +to be programmed to understand them. Help alleviate this load +by reducing the set of data that dnsmasq pays attention to +when we know there are events that are not useful at this +point in time. + +Signed-off-by: Donald Sharp +(cherry picked from commit b2ed691eb3ca6488a8878f5f3dd950a07b14a9db) +--- + src/netlink.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/netlink.c b/src/netlink.c +index 8cd51af..0a3da3e 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -363,7 +363,9 @@ static void nl_async(struct nlmsghdr *h) + failing. */ + struct rtmsg *rtm = NLMSG_DATA(h); + +- if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK) ++ if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && ++ (rtm->rtm_table == RT_TABLE_MAIN || ++ rtm->rtm_table == RT_TABLE_LOCAL)) + queue_event(EVENT_NEWROUTE); + } + else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) +-- +2.26.2 + diff --git a/SOURCES/dnsmasq-2.84-bind-dynamic-netlink.patch b/SOURCES/dnsmasq-2.84-bind-dynamic-netlink.patch new file mode 100644 index 0000000..f325e6d --- /dev/null +++ b/SOURCES/dnsmasq-2.84-bind-dynamic-netlink.patch @@ -0,0 +1,237 @@ +From 5010c42c47b7b5a3d68d83369d6c17ed0bc11cff Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Wed, 17 Feb 2021 11:47:28 +0100 +Subject: [PATCH] Correct occasional --bind-dynamic synchronization break + +Request only one re-read of addresses and/or routes + +Previous implementation re-reads systemd addresses exactly the same +number of time equal number of notifications received. +This is not necessary, we need just notification of change, then re-read +the current state and adapt listeners. Repeated re-reading slows netlink +processing and highers CPU usage on mass interface changes. + +Continue reading multicast events from netlink, even when ENOBUFS +arrive. Broadcasts are not trusted anyway and refresh would be done in +iface_enumerate. Save queued events sent again. + +Remove sleeping on netlink ENOBUFS + +With reduced number of written events netlink should receive ENOBUFS +rarely. It does not make sense to wait if it is received. It is just a +signal some packets got missing. Fast reading all pending packets is required, +seq checking ensures it already. Finishes changes by +commit 1d07667ac77c55b9de56b1b2c385167e0e0ec27a. + +Move restart from iface_enumerate to enumerate_interfaces + +When ENOBUFS is received, restart of reading addresses is done. But +previously found addresses might not have been found this time. In order +to catch this, restart both IPv4 and IPv6 enumeration with clearing +found interfaces first. It should deliver up-to-date state also after +ENOBUFS. + +Read all netlink messages before netlink restart + +Before writing again into netlink socket, try fetching all pending +messages. They would be ignored, only might trigger new address +synchronization. Should ensure new try has better chance to succeed. + +Request sending ENOBUFS again + +ENOBUFS error handling was improved. Netlink is correctly drained before +sending a new request again. It seems ENOBUFS supression is no longer +necessary or wanted. Let kernel tell us when it failed and handle it a +good way. +--- + src/netlink.c | 67 ++++++++++++++++++++++++++++++++++++--------------- + src/network.c | 11 +++++++-- + 2 files changed, 57 insertions(+), 21 deletions(-) + +diff --git a/src/netlink.c b/src/netlink.c +index ac1a1c5..f95f3e8 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -32,13 +32,21 @@ + + #ifndef NDA_RTA + # define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +-#endif ++#endif ++ ++/* Used to request refresh of addresses or routes just once, ++ * when multiple changes might be announced. */ ++enum async_states { ++ STATE_NEWADDR = (1 << 0), ++ STATE_NEWROUTE = (1 << 1), ++}; + + + static struct iovec iov; + static u32 netlink_pid; + +-static void nl_async(struct nlmsghdr *h); ++static unsigned nl_async(struct nlmsghdr *h, unsigned state); ++static void nl_multicast_state(unsigned state); + + void netlink_init(void) + { +@@ -135,7 +143,9 @@ static ssize_t netlink_recv(void) + + + /* family = AF_UNSPEC finds ARP table entries. +- family = AF_LOCAL finds MAC addresses. */ ++ family = AF_LOCAL finds MAC addresses. ++ returns 0 on failure, 1 on success, -1 when restart is required ++*/ + int iface_enumerate(int family, void *parm, int (*callback)()) + { + struct sockaddr_nl addr; +@@ -143,6 +153,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + ssize_t len; + static unsigned int seq = 0; + int callback_ok = 1; ++ unsigned state = 0; + + struct { + struct nlmsghdr nlh; +@@ -154,7 +165,6 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + addr.nl_groups = 0; + addr.nl_pid = 0; /* address to kernel */ + +- again: + if (family == AF_UNSPEC) + req.nlh.nlmsg_type = RTM_GETNEIGH; + else if (family == AF_LOCAL) +@@ -181,8 +191,8 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + { + if (errno == ENOBUFS) + { +- sleep(1); +- goto again; ++ nl_multicast_state(state); ++ return -1; + } + return 0; + } +@@ -191,7 +201,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) + { + /* May be multicast arriving async */ +- nl_async(h); ++ state = nl_async(h, state); + } + else if (h->nlmsg_seq != seq) + { +@@ -327,26 +337,36 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + } + } + +-void netlink_multicast(void) ++static void nl_multicast_state(unsigned state) + { + ssize_t len; + struct nlmsghdr *h; + int flags; +- +- /* don't risk blocking reading netlink messages here. */ ++ + if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || + fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) + return; ++ ++ do { ++ /* don't risk blocking reading netlink messages here. */ ++ while ((len = netlink_recv()) != -1) + +- if ((len = netlink_recv()) != -1) +- for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) +- nl_async(h); +- ++ for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) ++ state = nl_async(h, state); ++ } while (errno == ENOBUFS); ++ + /* restore non-blocking status */ + fcntl(daemon->netlinkfd, F_SETFL, flags); + } + +-static void nl_async(struct nlmsghdr *h) ++void netlink_multicast(void) ++{ ++ unsigned state = 0; ++ nl_multicast_state(state); ++} ++ ++ ++static unsigned nl_async(struct nlmsghdr *h, unsigned state) + { + if (h->nlmsg_type == NLMSG_ERROR) + { +@@ -354,7 +374,8 @@ static void nl_async(struct nlmsghdr *h) + if (err->error != 0) + my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); + } +- else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) ++ else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE && ++ (state & STATE_NEWROUTE)==0) + { + /* We arrange to receive netlink multicast messages whenever the network route is added. + If this happens and we still have a DNS packet in the buffer, we re-send it. +@@ -366,10 +387,18 @@ static void nl_async(struct nlmsghdr *h) + if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && + (rtm->rtm_table == RT_TABLE_MAIN || + rtm->rtm_table == RT_TABLE_LOCAL)) +- queue_event(EVENT_NEWROUTE); ++ { ++ queue_event(EVENT_NEWROUTE); ++ state |= STATE_NEWROUTE; ++ } ++ } ++ else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) && ++ (state & STATE_NEWADDR)==0) ++ { ++ queue_event(EVENT_NEWADDR); ++ state |= STATE_NEWADDR; + } +- else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) +- queue_event(EVENT_NEWADDR); ++ return state; + } + #endif + +diff --git a/src/network.c b/src/network.c +index c6e7d89..47caf38 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -656,7 +656,8 @@ int enumerate_interfaces(int reset) + + if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) + return 0; +- ++ ++again: + /* Mark interfaces for garbage collection */ + for (iface = daemon->interfaces; iface; iface = iface->next) + iface->found = 0; +@@ -709,10 +710,16 @@ int enumerate_interfaces(int reset) + + #ifdef HAVE_IPV6 + ret = iface_enumerate(AF_INET6, ¶m, iface_allowed_v6); ++ if (ret < 0) ++ goto again; + #endif + + if (ret) +- ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4); ++ { ++ ret = iface_enumerate(AF_INET, ¶m, iface_allowed_v4); ++ if (ret < 0) ++ goto again; ++ } + + errsave = errno; + close(param.fd); +-- +2.26.2 + diff --git a/SPECS/dnsmasq.spec b/SPECS/dnsmasq.spec index f3ea45a..e0d3f4e 100644 --- a/SPECS/dnsmasq.spec +++ b/SPECS/dnsmasq.spec @@ -13,7 +13,7 @@ Name: dnsmasq Version: 2.76 -Release: 16%{?extraversion}%{?dist}.1 +Release: 17%{?extraversion}%{?dist}.1 Summary: A lightweight DHCP/caching DNS server Group: System Environment/Daemons @@ -83,6 +83,10 @@ Patch36: dnsmasq-2.79-CVE-2020-25685.patch Patch37: dnsmasq-2.79-CVE-2020-25686.patch # Link hash function from nettle, avoid local implementation Patch38: dnsmasq-2.76-nettlehash.patch +# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=1d07667ac77c55b9de56b1b2c385167e0e0ec27a +Patch39: dnsmasq-2.77-netlink-loop.patch +Patch40: dnsmasq-2.81-netlink-table.patch +Patch41: dnsmasq-2.84-bind-dynamic-netlink.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -157,6 +161,9 @@ query/remove a DHCP server's leases. %patch36 -p1 -b .CVE-2020-25685 %patch37 -p1 -b .CVE-2020-25686 %patch38 -p1 -b .nettlehash +%patch39 -p1 -b .rh1887649-seq +%patch40 -p1 -b .rh1887649-table +%patch41 -p1 -b .rh1887649 # use /var/lib/dnsmasq instead of /var/lib/misc for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do @@ -246,6 +253,9 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man1/dhcp_* %changelog +* Thu Feb 11 2021 Petr Menšík - 2.76-17.1 +- Fix occasional failures in netlink on many interfaces (#1887649) + * Fri Nov 27 2020 Petr Menšík - 2.76-16.1 - Accept responses only on correct sockets (CVE-2020-25684) - Use strong verification on queries (CVE-2020-25685)