]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
BSD: Add support for RO_MISSFILTER route(4) socket option
authorRoy Marples <roy@marples.name>
Sat, 8 Feb 2020 17:29:03 +0000 (17:29 +0000)
committerRoy Marples <roy@marples.name>
Sat, 8 Feb 2020 17:30:35 +0000 (17:30 +0000)
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.

src/dhcpcd.h
src/if-bsd.c
src/if.h
src/route.c

index 6242956a0f7a9cab49699c2f8ab0a60f829058e2..ac61d926e4ab8ba5c9d45908d179b14936ab8114 100644 (file)
@@ -183,6 +183,13 @@ struct dhcpcd_ctx {
 
        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;
index 76ba114e34e23be6d83312fb0bf9671ad2e563c4..0e6318192ca6fa188dc0b374c2b89effc1530dcb 100644 (file)
@@ -1509,6 +1509,78 @@ if_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
        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)
index 0656affe858e57463777bd9e7bad17022d4c4176..bac109d6e0206fe55e59f7effbfda37b5941b20c 100644 (file)
--- a/src/if.h
+++ b/src/if.h
@@ -206,6 +206,9 @@ int if_setmac(struct interface *ifp, void *, uint8_t);
 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 *,
index 56e57a31f7524f835c3cfa789817437f94c6aca0..1b9c26f022048427f65d89753d67a460e7e82e71 100644 (file)
@@ -690,22 +690,26 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
        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 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
                }
        }
 
+#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 &&