]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
kroutes initial
authorRoy Marples <roy@marples.name>
Tue, 1 Apr 2025 12:32:32 +0000 (13:32 +0100)
committerRoy Marples <roy@marples.name>
Tue, 1 Apr 2025 12:32:32 +0000 (13:32 +0100)
src/dhcpcd.c
src/dhcpcd.h
src/if-bsd.c
src/if.h
src/route.c
src/route.h

index 7cea7bd5f061ac4110439559c1a6b8471de5aed6..0fc3b25acb1787b44bf9a3341b40d9c667e2847f 100644 (file)
@@ -2587,6 +2587,7 @@ start_manager:
                }
        }
 
+       rt_discover(&ctx);
        TAILQ_FOREACH(ifp, ctx.ifaces, next) {
                if (ifp->active)
                        dhcpcd_initstate1(ifp, argc, argv, 0);
index 2c5df6d12a0f3cbd95d8535d523aef38276baae0..d8ef5d99b62e9297d8b497473886d58871a5d8f4 100644 (file)
@@ -142,6 +142,7 @@ struct dhcpcd_ctx {
        size_t ctl_extra;
 
        rb_tree_t routes;       /* our routes */
+       rb_tree_t kroutes;      /* kernel routes */
 #ifdef RT_FREE_ROUTE_TABLE
        rb_tree_t froutes;      /* free routes for re-use */
 #endif
index bb9c223a84f4fca58a2dbd7e236c42374e98c8e9..c0471197e7dc959b00231304ac5c6065aaebb522 100644 (file)
@@ -866,24 +866,45 @@ if_realroute(const struct rt_msghdr *rtm)
        return true;
 }
 
-static int
-if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
+static struct rt *
+if_rtmtort(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
        const struct sockaddr *rti_info[RTAX_MAX];
+       struct interface *ifp;
+       struct rt *rt;
 
        if (!(rtm->rtm_addrs & RTA_DST)) {
                errno = EINVAL;
-               return -1;
+               return NULL;
        }
        if (rtm->rtm_type != RTM_MISS && !(rtm->rtm_addrs & RTA_GATEWAY)) {
                errno = EINVAL;
-               return -1;
+               return NULL;
        }
 
        if (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),
                      rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
-               return -1;
-       memset(rt, 0, sizeof(*rt));
+               return NULL;
+
+       if (rtm->rtm_index)
+               ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
+       else if (rtm->rtm_addrs & RTA_IFP)
+               ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
+       else if (rtm->rtm_addrs & RTA_GATEWAY)
+               ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
+       else
+               ifp = if_findsa(ctx, rti_info[RTAX_DST]);
+
+       if (ifp == NULL && rtm->rtm_type == RTM_MISS)
+               ifp = if_find(ctx->ifaces, "lo0");
+
+       if (ifp == NULL) {
+               errno = ESRCH;
+               return NULL;
+       }
+
+       rt = rt_new0(ctx);
+       rt->rt_ifp = ifp;
 
        rt->rt_flags = (unsigned int)rtm->rtm_flags;
        if_copysa(&rt->rt_dest, rti_info[RTAX_DST]);
@@ -924,23 +945,7 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
 
        rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
 
-       if (rtm->rtm_index)
-               rt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
-       else if (rtm->rtm_addrs & RTA_IFP)
-               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
-       else if (rtm->rtm_addrs & RTA_GATEWAY)
-               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
-       else
-               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);
-
-       if (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS)
-               rt->rt_ifp = if_find(ctx->ifaces, "lo0");
-
-       if (rt->rt_ifp == NULL) {
-               errno = ESRCH;
-               return -1;
-       }
-       return 0;
+       return rt;
 }
 
 static int
@@ -960,13 +965,13 @@ if_sysctl(struct dhcpcd_ctx *ctx,
 }
 
 int
-if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
+if_initrt(struct dhcpcd_ctx *ctx, int af)
 {
        struct rt_msghdr *rtm;
        int mib[6] = { CTL_NET, PF_ROUTE, 0, af, NET_RT_DUMP, 0 };
        size_t bufl;
        char *buf = NULL, *p, *end;
-       struct rt rt, *rtn;
+       struct rt *rt;
 
 again:
        if (if_sysctl(ctx, mib, __arraycount(mib), NULL, &bufl, NULL, 0) == -1)
@@ -994,15 +999,10 @@ again:
                }
                if (!if_realroute(rtm))
                        continue;
-               if (if_copyrt(ctx, &rt, rtm) != 0)
+               if ((rt = if_rtmtort(ctx, rtm)) == NULL)
                        continue;
-               if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
-                       logerr(__func__);
-                       break;
-               }
-               memcpy(rtn, &rt, sizeof(*rtn));
-               if (rb_tree_insert_node(kroutes, rtn) != rtn)
-                       rt_free(rtn);
+               if (rb_tree_insert_node(&ctx->kroutes, rt) != rt)
+                       rt_free(rt);
        }
        free(buf);
        return p == end ? 0 : -1;
@@ -1282,7 +1282,7 @@ if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)
 static int
 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
-       struct rt rt;
+       struct rt *rt;
 
        if (rtm->rtm_msglen < sizeof(*rtm)) {
                errno = EINVAL;
@@ -1301,7 +1301,7 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
        }
 #endif
 
-       if (if_copyrt(ctx, &rt, rtm) == -1)
+       if ((rt = if_rtmtort(ctx, rtm)) == NULL)
                return errno == ENOTSUP ? 0 : -1;
 
 #ifdef INET6
@@ -1311,21 +1311,21 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
         * existance with a hardware address.
         * Ensure we don't call this for a newly incomplete state.
         */
-       if (rt.rt_dest.sa_family == AF_INET6 &&
-           (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
-           !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK)))
+       if (rt->rt_dest.sa_family == AF_INET6 &&
+           (rt->rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
+           !(rtm->rtm_type == RTM_ADD && !(rt->rt_dflags & RTDF_GATELINK)))
        {
                bool reachable;
 
                reachable = (rtm->rtm_type == RTM_ADD ||
                    rtm->rtm_type == RTM_CHANGE) &&
-                   rt.rt_dflags & RTDF_GATELINK;
-               ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);
+                   rt->rt_dflags & RTDF_GATELINK;
+               ipv6nd_neighbour(ctx, &rt->rt_ss_dest.sin6.sin6_addr, reachable);
        }
 #endif
 
        if (rtm->rtm_type != RTM_MISS && if_realroute(rtm))
-               rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
+               rt_recvrt(rtm->rtm_type, rt, rtm->rtm_pid);
        return 0;
 }
 
index 8d1aeccff814f7b5019132370e9dd669e89964e8..80c47e97cb55e43ba5a02672df34f63e2aa826c9 100644 (file)
--- a/src/if.h
+++ b/src/if.h
@@ -249,7 +249,7 @@ int xsocket(int, int, int);
 int xsocketpair(int, int, int, int[2]);
 
 int if_route(unsigned char, const struct rt *rt);
-int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
+int if_initrt(struct dhcpcd_ctx *, int);
 
 int if_missfilter(struct interface *, struct sockaddr *);
 int if_missfilter_apply(struct dhcpcd_ctx *);
index 0037dda4ff0d832e58791aadde21f1e9fd37f1d8..2b1e038cf061b4b9046765a6498257b92a107c47 100644 (file)
@@ -237,11 +237,21 @@ static const rb_tree_ops_t rt_compare_free_ops = {
 };
 #endif
 
+void
+rt_discover(struct dhcpcd_ctx *ctx)
+{
+
+       rt_headclear(&ctx->kroutes, AF_UNSPEC);
+       if (if_initrt(ctx, AF_UNSPEC) != 0)
+               logerr("%s: if_initrt", __func__);
+}
+
 void
 rt_init(struct dhcpcd_ctx *ctx)
 {
 
        rb_tree_init(&ctx->routes, &rt_compare_os_ops);
+       rb_tree_init(&ctx->kroutes, &rt_compare_os_ops);
 #ifdef RT_FREE_ROUTE_TABLE
        rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
 #endif
@@ -347,6 +357,7 @@ rt_dispose(struct dhcpcd_ctx *ctx)
 
        assert(ctx != NULL);
        rt_headfree(&ctx->routes);
+       rt_headfree(&ctx->kroutes);
 #ifdef RT_FREE_ROUTE_TABLE
        rt_headfree(&ctx->froutes);
 #ifdef RT_FREE_ROUTE_TABLE_STATS
@@ -372,11 +383,10 @@ rt_new0(struct dhcpcd_ctx *ctx)
 #endif
        } else
 #endif
-       if ((rt = malloc(sizeof(*rt))) == NULL) {
+       if (rt == NULL && (rt = malloc(sizeof(*rt))) == NULL)
                logerr(__func__);
-               return NULL;
-       }
-       memset(rt, 0, sizeof(*rt));
+       else
+               memset(rt, 0, sizeof(*rt));
        return rt;
 }
 
@@ -472,10 +482,11 @@ rt_freeif(struct interface *ifp)
 /* If something other than dhcpcd removes a route,
  * we need to remove it from our internal table. */
 void
-rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
+rt_recvrt(int cmd, struct rt *rt, pid_t pid)
 {
        struct dhcpcd_ctx *ctx;
        struct rt *f;
+       bool freert = false;
 
        assert(rt != NULL);
        assert(rt->rt_ifp != NULL);
@@ -484,6 +495,25 @@ rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
        ctx = rt->rt_ifp->ctx;
 
        switch(cmd) {
+       case RTM_ADD:           /* FALLTHROUGH */
+       case RTM_CHANGE:        /* FALLTHROUGH */
+       case RTM_DELETE:
+               /* For any of these actions, we delete any matching
+                * kernel route we have. */
+               f = rb_tree_find_node(&ctx->kroutes, rt);
+               if (f != NULL) {
+                       rb_tree_remove_node(&ctx->kroutes, f);
+                       rt_free(f);
+               }
+               break;
+       }
+
+       switch(cmd) {
+       case RTM_ADD:           /* FALLTHROUGH */
+       case RTM_CHANGE:
+               if (rb_tree_insert_node(&ctx->kroutes, rt) != rt) /* safety */
+                       freert = true;
+               break;
        case RTM_DELETE:
                f = rb_tree_find_node(&ctx->routes, rt);
                if (f != NULL) {
@@ -501,6 +531,9 @@ rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
        if (rt->rt_dest.sa_family == AF_INET)
                ipv4ll_recvrt(cmd, rt);
 #endif
+
+       if (freert)
+               rt_free(rt);
 }
 
 /* Compare miscellaneous route details */
@@ -553,12 +586,12 @@ rt_cmp_lifetime(struct rt *nrt, struct rt *ort)
 #endif
 
 static bool
-rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
+rt_add(struct rt *nrt, struct rt *ort)
 {
        struct dhcpcd_ctx *ctx;
        struct rt *krt;
        int loglevel = LOG_INFO;
-       bool change, result;
+       bool change;
 
        assert(nrt != NULL);
        ctx = nrt->rt_ifp->ctx;
@@ -578,7 +611,7 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
            sa_is_unspecified(&nrt->rt_netmask))
                return false;
 
-       krt = rb_tree_find_node(kroutes, nrt);
+       krt = rb_tree_find_node(&ctx->kroutes, nrt);
        if (krt != NULL &&
            krt->rt_ifp == nrt->rt_ifp &&
            /* Only test flags dhcpcd controls */
@@ -616,10 +649,8 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
 #endif
 
        if (change) {
-               if (if_route(RTM_CHANGE, nrt) != -1) {
-                       result = true;
-                       goto out;
-               }
+               if (if_route(RTM_CHANGE, nrt) != -1)
+                       return true;
                if (errno != ESRCH)
                        logerr("if_route (CHG)");
        }
@@ -631,9 +662,12 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
                if (krt != NULL) {
                        if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)
                                logerr("if_route (DEL)");
+                       else {
+                               rb_tree_remove_node(&ctx->kroutes, krt);
+                               rt_free(krt);
+                       }
                }
-               result = true;
-               goto out;
+               return true;
        }
 
        /* If the kernel claims the route exists we need to rip out the
@@ -650,6 +684,10 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
        if (krt != NULL) {
                if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)
                        logerr("if_route (DEL)");
+               else {
+                       rb_tree_remove_node(&ctx->kroutes, krt);
+                       rt_free(krt);
+               }
        }
 #ifdef ROUTE_PER_GATEWAY
        /* The OS allows many routes to the same dest with different gateways.
@@ -665,22 +703,14 @@ rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
 
        /* Shouldn't need to check for EEXIST, but some kernels don't
         * dump the subnet route just after we added the address. */
-       if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
-               result = true;
-               goto out;
-       }
+       if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST)
+               return true;
 
 #ifdef HAVE_ROUTE_METRIC
 logerr:
 #endif
        logerr("if_route (ADD)");
-
-out:
-       if (krt != NULL) {
-               rb_tree_remove_node(kroutes, krt);
-               rt_free(krt);
-       }
-       return result;
+       return false;
 }
 
 static bool
@@ -709,7 +739,7 @@ rt_cmp(const struct rt *r1, const struct rt *r2)
 }
 
 static bool
-rt_doroute(rb_tree_t *kroutes, struct rt *rt)
+rt_doroute(struct rt *rt)
 {
        struct dhcpcd_ctx *ctx;
        struct rt *or;
@@ -729,20 +759,20 @@ rt_doroute(rb_tree_t *kroutes, struct rt *rt)
 #endif
                    rt_cmp_mtu(rt, or) != 0)
                {
-                       if (!rt_add(kroutes, rt, or))
+                       if (!rt_add(rt, or))
                                return false;
                }
                rb_tree_remove_node(&ctx->routes, or);
                rt_free(or);
        } else {
                if (rt->rt_dflags & RTDF_FAKE) {
-                       or = rb_tree_find_node(kroutes, rt);
+                       or = rb_tree_find_node(&ctx->kroutes, rt);
                        if (or == NULL)
                                return false;
                        if (rt_cmp(rt, or) == 0)
                                return false;
                } else {
-                       if (!rt_add(kroutes, rt, NULL))
+                       if (!rt_add(rt, NULL))
                                return false;
                }
        }
@@ -753,15 +783,12 @@ rt_doroute(rb_tree_t *kroutes, struct rt *rt)
 void
 rt_build(struct dhcpcd_ctx *ctx, int af)
 {
-       rb_tree_t routes, added, kroutes;
+       rb_tree_t routes, added;
        struct rt *rt, *rtn;
        unsigned long long o;
 
        rb_tree_init(&routes, &rt_compare_proto_ops);
        rb_tree_init(&added, &rt_compare_os_ops);
-       rb_tree_init(&kroutes, &rt_compare_os_ops);
-       if (if_initrt(ctx, &kroutes, af) != 0)
-               logerr("%s: if_initrt", __func__);
        ctx->rt_order = 0;
        ctx->options |= DHCPCD_RTBUILD;
 
@@ -798,7 +825,7 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
                /* Is this route already in our table? */
                if (rb_tree_find_node(&added, rt) != NULL)
                        continue;
-               if (rt_doroute(&kroutes, rt)) {
+               if (rt_doroute(rt)) {
                        rb_tree_remove_node(&routes, rt);
                        if (rb_tree_insert_node(&added, rt) != rt) {
                                errno = EEXIST;
@@ -845,5 +872,4 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
 
 getfail:
        rt_headclear(&routes, AF_UNSPEC);
-       rt_headclear(&kroutes, AF_UNSPEC);
 }
index eea7e559fc99406cf6115c3322915119fcf47624..20b6432628eec6d4771722561c9a1961dcfe8f2c 100644 (file)
@@ -134,6 +134,7 @@ extern const rb_tree_ops_t rt_compare_list_ops;
 extern const rb_tree_ops_t rt_compare_proto_ops;
 
 void rt_init(struct dhcpcd_ctx *);
+void rt_discover(struct dhcpcd_ctx *);
 void rt_dispose(struct dhcpcd_ctx *);
 void rt_free(struct rt *);
 void rt_freeif(struct interface *);
@@ -147,7 +148,7 @@ struct rt * rt_new(struct interface *);
 struct rt * rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *);
 struct rt * rt_proto_add(rb_tree_t *, struct rt *);
 int rt_cmp_dest(const struct rt *, const struct rt *);
-void rt_recvrt(int, const struct rt *, pid_t);
+void rt_recvrt(int, struct rt *, pid_t);
 void rt_build(struct dhcpcd_ctx *, int);
 
 #endif