changeset 5057:a5fd2097d40f draft

BSD: Add support for RO_MISSFILTER route(4) socket option This allows dhcpcd to only listen for RTM_MISS generated by default routers dhcpcd *could* install so if one becomes unreachable we can pick another.
author Roy Marples <roy@marples.name>
date Sat, 08 Feb 2020 17:29:03 +0000
parents f402f3ef7c23
children 607d2d75b835
files src/dhcpcd.h src/if-bsd.c src/if.h src/route.c
diffstat 4 files changed, 101 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/dhcpcd.h	Fri Feb 07 20:56:11 2020 +0000
+++ b/src/dhcpcd.h	Sat Feb 08 17:29:03 2020 +0000
@@ -183,6 +183,13 @@
 
 	char *randomstate; /* original state */
 
+	/* For filtering RTM_MISS messages per router */
+#ifdef BSD
+	uint8_t *rt_missfilter;
+	size_t rt_missfilterlen;
+	size_t rt_missfiltersize;
+#endif
+
 #ifdef PRIVSEP
 	struct passwd *ps_user;	/* struct passwd for privsep user */
 	pid_t ps_root_pid;
--- a/src/if-bsd.c	Fri Feb 07 20:56:11 2020 +0000
+++ b/src/if-bsd.c	Sat Feb 08 17:29:03 2020 +0000
@@ -1509,6 +1509,78 @@
 	return 0;
 }
 
+static int
+if_missfilter0(struct dhcpcd_ctx *ctx, struct interface *ifp,
+    struct sockaddr *sa)
+{
+	size_t salen = (size_t)RT_ROUNDUP(sa->sa_len);
+	size_t newlen = ctx->rt_missfilterlen + salen;
+	size_t diff = salen - (sa->sa_len);
+	uint8_t *cp;
+
+	if (ctx->rt_missfiltersize < newlen) {
+		void *n = realloc(ctx->rt_missfilter, newlen);
+		if (n == NULL)
+			return -1;
+		ctx->rt_missfilter = n;
+		ctx->rt_missfiltersize = newlen;
+	}
+
+#ifdef INET6
+	if (sa->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+		ifa_setscope(sin6, ifp->index);
+	}
+#endif
+
+	cp = ctx->rt_missfilter + ctx->rt_missfilterlen;
+	memcpy(cp, sa, sa->sa_len);
+	if (diff != 0)
+		memset(cp + sa->sa_len, 0, diff);
+	ctx->rt_missfilterlen += salen;
+
+#ifdef INET6
+	if (sa->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+		ifa_setscope(sin6, 0);
+	}
+#endif
+
+	return 0;
+}
+
+int
+if_missfilter(struct interface *ifp, struct sockaddr *sa)
+{
+
+	return if_missfilter0(ifp->ctx, ifp, sa);
+}
+
+int
+if_missfilter_apply(struct dhcpcd_ctx *ctx)
+{
+#ifdef RO_MISSFILTER
+	if (ctx->rt_missfilterlen == 0) {
+		struct sockaddr sa = {
+		    .sa_family = AF_UNSPEC,
+		    .sa_len = sizeof(sa),
+		};
+
+		if (if_missfilter0(ctx, NULL, &sa) == -1)
+			return -1;
+	}
+
+	return setsockopt(ctx->link_fd, PF_ROUTE, RO_MISSFILTER,
+	    ctx->rt_missfilter, (socklen_t)ctx->rt_missfilterlen);
+#else
+#warning kernel does not support RTM_MISS DST filtering
+	errno = ENOTSUP;
+	return -1;
+#endif
+}
+
 __CTASSERT(offsetof(struct rt_msghdr, rtm_msglen) == 0);
 int
 if_handlelink(struct dhcpcd_ctx *ctx)
--- a/src/if.h	Fri Feb 07 20:56:11 2020 +0000
+++ b/src/if.h	Sat Feb 08 17:29:03 2020 +0000
@@ -206,6 +206,9 @@
 int if_route(unsigned char, const struct rt *rt);
 int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
 
+int if_missfilter(struct interface *, struct sockaddr *);
+int if_missfilter_apply(struct dhcpcd_ctx *);
+
 #ifdef INET
 int if_address(unsigned char, const struct ipv4_addr *);
 int if_addrflags(const struct interface *, const struct in_addr *,
--- a/src/route.c	Fri Feb 07 20:56:11 2020 +0000
+++ b/src/route.c	Sat Feb 08 17:29:03 2020 +0000
@@ -690,22 +690,26 @@
 	ctx->rt_order = 0;
 	ctx->options |= DHCPCD_RTBUILD;
 
-	switch (af) {
 #ifdef INET
-	case AF_INET:
-		if (!inet_getroutes(ctx, &routes))
-			goto getfail;
-		break;
+	if (!inet_getroutes(ctx, &routes))
+		goto getfail;
 #endif
 #ifdef INET6
-	case AF_INET6:
-		if (!inet6_getroutes(ctx, &routes))
-			goto getfail;
-		break;
+	if (!inet6_getroutes(ctx, &routes))
+		goto getfail;
 #endif
-	}
+
+#ifdef BSD
+	/* Rewind the miss filter */
+	ctx->rt_missfilterlen = 0;
+#endif
 
 	RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
+#ifdef BSD
+		if (rt_is_default(rt) &&
+		    if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
+			logerr("if_missfilter");
+#endif
 		if ((rt->rt_dest.sa_family != af &&
 		    rt->rt_dest.sa_family != AF_UNSPEC) ||
 		    (rt->rt_gateway.sa_family != af &&
@@ -724,6 +728,11 @@
 		}
 	}
 
+#ifdef BSD
+	if (if_missfilter_apply(ctx) == -1)
+		logerr("if_missfilter_apply");
+#endif
+
 	/* Remove old routes we used to manage. */
 	RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
 		if ((rt->rt_dest.sa_family != af &&