diff mbox series

[odhcp6c] ra: exit on RTM_DELLINK event

Message ID 1602672369-15035-1-git-send-email-alin.nastac@gmail.com
State Under Review
Delegated to: Hans Dedecker
Headers show
Series [odhcp6c] ra: exit on RTM_DELLINK event | expand

Commit Message

Alin Nastac Oct. 14, 2020, 10:46 a.m. UTC
Also handle the netlink message correctly (the current code assumes
that the entire buffer returned by recv contains just one netlink
message).

This fixes a timing issue that occurs when wan interface proto is
pppoe, wan6 proto is dhcpv6 and PPP session is closed by the peer.

Because odhpc6c doesn't react to pppoe-wan device deletion, sometimes
netifd device created by the old pppd instance doesn't get released
before new pppd instance execute the ppp-up script (pppoe-wan device
is reffered by wan6 device alias), so device_claim() doesn't trigger
device_set_ifindex(). That will impede default route creation for wan
interface (pppoe-wan device will store the incorrect ifindex).

Signed-off-by: Alin Nastac <alin.nastac@gmail.com>
---
 src/dhcpv6.c |  6 +++++-
 src/ra.c     | 54 ++++++++++++++++++++++++++++++------------------------
 2 files changed, 35 insertions(+), 25 deletions(-)
diff mbox series

Patch

diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index bd8a2dc..4cd1abc 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -560,7 +560,11 @@  int dhcpv6_request(enum dhcpv6_msg type)
 		struct timespec ts = {0, 0};
 		ts.tv_nsec = (dhcpv6_rand_delay((10000 * DHCPV6_REQ_DELAY) / 2) + (1000 * DHCPV6_REQ_DELAY) / 2) * 1000000;
 
-		while (nanosleep(&ts, &ts) < 0 && errno == EINTR);
+		while (nanosleep(&ts, &ts) < 0 && errno == EINTR) {
+			// Check for pending signal
+			if (odhcp6c_signal_process())
+				return -1;
+		}
 	}
 
 	if (type == DHCPV6_MSG_UNKNOWN)
diff --git a/src/ra.c b/src/ra.c
index 337c0bd..9af48a2 100644
--- a/src/ra.c
+++ b/src/ra.c
@@ -16,6 +16,7 @@ 
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <signal.h>
 #include <string.h>
 #include <stddef.h>
@@ -208,37 +209,42 @@  static int16_t pref_to_priority(uint8_t flags)
 bool ra_link_up(void)
 {
 	static bool firstcall = true;
-	struct {
-		struct nlmsghdr hdr;
-		struct ifinfomsg msg;
-		uint8_t pad[4000];
-	} resp;
+	char buf[4096];
 	bool ret = false;
 	ssize_t read;
 
-	do {
-		read = recv(rtnl, &resp, sizeof(resp), MSG_DONTWAIT);
+	while ((read = recv(rtnl, &buf, sizeof(buf), MSG_DONTWAIT)) > 0) {
+		for (struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+				NLMSG_OK (nlh, read) && nlh->nlmsg_type != NLMSG_DONE;
+				nlh = NLMSG_NEXT(nlh, read)) {
+			if (nlh->nlmsg_type != RTM_NEWLINK && nlh->nlmsg_type != RTM_DELLINK)
+				continue;
 
-		if (read < 0 || !NLMSG_OK(&resp.hdr, (size_t)read) ||
-				resp.hdr.nlmsg_type != RTM_NEWLINK ||
-				resp.msg.ifi_index != if_index)
-			continue;
+			struct ifinfomsg *ifi = NLMSG_DATA(nlh);
+			if (ifi->ifi_index != if_index)
+				continue;
 
-		ssize_t alen = NLMSG_PAYLOAD(&resp.hdr, sizeof(resp.msg));
-		for (struct rtattr *rta = (struct rtattr*)(resp.pad);
-				RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen)) {
-			if (rta->rta_type == IFLA_ADDRESS &&
-					RTA_PAYLOAD(rta) >= sizeof(rs.lladdr.data))
-				memcpy(rs.lladdr.data, RTA_DATA(rta), sizeof(rs.lladdr.data));
-		}
+			if (nlh->nlmsg_type == RTM_DELLINK) {
+				syslog(LOG_ERR, "Interface %s has been deleted, exiting", if_name);
+				exit(1);
+			}
 
-		bool hascarrier = resp.msg.ifi_flags & IFF_LOWER_UP;
-		if (!firstcall && nocarrier != !hascarrier)
-			ret = true;
+			ssize_t alen = IFLA_PAYLOAD(nlh);
+			for (struct rtattr *rta = IFLA_RTA(nlh);
+					RTA_OK(rta, alen); rta = RTA_NEXT(rta, alen)) {
+				if (rta->rta_type == IFLA_ADDRESS &&
+						RTA_PAYLOAD(rta) >= sizeof(rs.lladdr.data))
+					memcpy(rs.lladdr.data, RTA_DATA(rta), sizeof(rs.lladdr.data));
+			}
+
+			bool hascarrier = ifi->ifi_flags & IFF_LOWER_UP;
+			if (!firstcall && nocarrier != !hascarrier)
+				ret = true;
 
-		nocarrier = !hascarrier;
-		firstcall = false;
-	} while (read > 0);
+			nocarrier = !hascarrier;
+			firstcall = false;
+		}
+	}
 
 	if (ret) {
 		syslog(LOG_NOTICE, "carrier => %i event on %s", (int)!nocarrier, if_name);