]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
route: Use order from message / config
authorRoy Marples <roy@marples.name>
Fri, 7 Jun 2019 15:37:00 +0000 (16:37 +0100)
committerRoy Marples <roy@marples.name>
Fri, 7 Jun 2019 15:37:00 +0000 (16:37 +0100)
We can't just make the order up as that is prone to error.
So apply ordering as from the source.

src/dhcp.c
src/dhcpcd.h
src/if-options.c
src/ipv4.c
src/ipv4.h
src/ipv4ll.c
src/ipv6.c
src/route.c
src/route.h

index f548223e6393aee11b18463c26b76e191cb46d21..23770073777cae4026200bdbf949bf759fb00e39 100644 (file)
@@ -468,9 +468,7 @@ decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp,
                sa_in_init(&rt->rt_dest, &dest);
                sa_in_init(&rt->rt_netmask, &netmask);
                sa_in_init(&rt->rt_gateway, &gateway);
-               if (rb_tree_insert_node(routes, rt) != rt)
-                       rt_free(rt);
-               else
+               if (rt_proto_add(routes, rt))
                        n++;
        }
        return n;
index 539a45816dccf70ce9182890dac092d422c9572c..7ce3cad303959b9d766cf8061c67bdd0cb39d0fb 100644 (file)
@@ -140,6 +140,7 @@ struct dhcpcd_ctx {
 #ifdef RT_FREE_ROUTE_TABLE
        rb_tree_t froutes;      /* free routes for re-use */
 #endif
+       size_t rt_order;        /* route order storage */
 
        int pf_inet_fd;
        void *priv;
index 8628dd5c518a57e33e9b8f893ae7bb2b68bf7201..e3d9c56ec363bf0c93f822cc2ce606182aa9e98b 100644 (file)
@@ -1129,9 +1129,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
                        sa_in_init(&rt->rt_dest, &addr);
                        sa_in_init(&rt->rt_netmask, &addr2);
                        sa_in_init(&rt->rt_gateway, &addr3);
-                       if (rb_tree_insert_node(&ifo->routes, rt) != rt)
-                               rt_free(rt);
-                       else
+                       if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
                                add_environ(&ifo->config, arg, 0);
                } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
                        if (parse_addr(&addr, NULL, p) == -1)
@@ -1142,9 +1140,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
                        sa_in_init(&rt->rt_dest, &addr2);
                        sa_in_init(&rt->rt_netmask, &addr2);
                        sa_in_init(&rt->rt_gateway, &addr);
-                       if (rb_tree_insert_node(&ifo->routes, rt) != rt)
-                               rt_free(rt);
-                       else
+                       if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
                                add_environ(&ifo->config, arg, 0);
                } else if (strncmp(arg, "interface_mtu=",
                    strlen("interface_mtu=")) == 0 ||
@@ -2282,7 +2278,7 @@ default_config(struct dhcpcd_ctx *ctx)
        ifo->script = UNCONST(default_script);
        ifo->metric = -1;
        ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
-       rb_tree_init(&ifo->routes, &rt_compare_list_ops);
+       rb_tree_init(&ifo->routes, &rt_compare_proto_ops);
 #ifdef AUTH
        TAILQ_INIT(&ifo->auth.tokens);
 #endif
@@ -2340,6 +2336,9 @@ read_config(struct dhcpcd_ctx *ctx,
        buf = NULL;
        buflen = 0;
 
+       /* Reset route order */
+       ctx->rt_order = 0;
+
        /* Parse our embedded options file */
        if (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) {
                /* Space for initial estimates */
index b63079bfd6c5e7fc80163a4a3b55e7222ea222ca..0cf5597f75b3009e5e83d6b1c5d8e6675b21cce4 100644 (file)
@@ -247,7 +247,7 @@ ipv4_ifcmp(const struct interface *si, const struct interface *ti)
 }
 
 static int
-inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
+inet_dhcproutes(rb_tree_t *routes, struct interface *ifp, bool *have_default)
 {
        const struct dhcp_state *state;
        rb_tree_t nroutes;
@@ -263,7 +263,7 @@ inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
        /* An address does have to exist. */
        assert(state->addr);
 
-       rb_tree_init(&nroutes, &rt_compare_list_ops);
+       rb_tree_init(&nroutes, &rt_compare_proto_ops);
 
        /* First, add a subnet route. */
        if (!(ifp->flags & IFF_POINTOPOINT) &&
@@ -283,8 +283,7 @@ inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
                //in.s_addr = INADDR_ANY;
                //sa_in_init(&rt->rt_gateway, &in);
                rt->rt_gateway.sa_family = AF_UNSPEC;
-               if (rb_tree_insert_node(&nroutes, rt) != rt)
-                       rt_free(rt);
+               rt_proto_add(&nroutes, rt);
        }
 
        /* If any set routes, grab them, otherwise DHCP routes. */
@@ -297,15 +296,14 @@ inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
                        memcpy(rt, r, sizeof(*rt));
                        rt_setif(rt, ifp);
                        rt->rt_dflags = RTDF_STATIC;
-                       if (rb_tree_insert_node(&nroutes, rt) != rt)
-                               rt_free(rt);
+                       rt_proto_add(&nroutes, rt);
                }
        } else {
                if (dhcp_get_routes(&nroutes, ifp) == -1)
                        return -1;
        }
 
-       /* If configured, Install a gateway to the desintion
+       /* If configured, install a gateway to the desintion
         * for P2P interfaces. */
        if (ifp->flags & IFF_POINTOPOINT &&
            has_option_mask(ifp->options->dstmask, DHO_ROUTER))
@@ -317,8 +315,7 @@ inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
                sa_in_init(&rt->rt_netmask, &in);
                sa_in_init(&rt->rt_gateway, &state->addr->brd);
                sa_in_init(&rt->rt_ifa, &state->addr->addr);
-               if (rb_tree_insert_node(&nroutes, rt) != rt)
-                       rt_free(rt);
+               rt_proto_add(&nroutes, rt);
        }
 
        /* Copy our address as the source address and set mtu */
@@ -330,10 +327,13 @@ inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
                if (!(rt->rt_dflags & RTDF_STATIC))
                        rt->rt_dflags |= RTDF_DHCP;
                sa_in_init(&rt->rt_ifa, &state->addr->addr);
-               if (rb_tree_insert_node(routes, rt) != rt)
+               if (rb_tree_insert_node(routes, rt) != rt) {
                        rt_free(rt);
-               else
-                       n++;
+                       continue;
+               }
+               if (rt_is_default(rt))
+                       *have_default = true;
+               n++;
        }
 
        return n;
@@ -425,25 +425,23 @@ inet_routerhostroute(rb_tree_t *routes, struct interface *ifp)
                sa_in_init(&rth->rt_gateway, &in);
                rth->rt_mtu = dhcp_get_mtu(ifp);
                sa_in_init(&rth->rt_ifa, &state->addr->addr);
-               if (rb_tree_insert_node(routes, rth) != rth)
-                       rt_free(rth);
+               rt_proto_add(routes, rt);
        }
        return 0;
 }
 
 bool
-inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes, rb_tree_t *kroutes)
+inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
 {
        struct interface *ifp;
 #ifdef IPV4LL
-       struct rt def;
-       bool have_default;
+       bool have_default = false;
 #endif
 
        TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                if (!ifp->active)
                        continue;
-               if (inet_dhcproutes(routes, ifp) == -1)
+               if (inet_dhcproutes(routes, ifp, &have_default) == -1)
                        return false;
 #ifdef IPV4LL
                if (ipv4ll_subnetroute(routes, ifp) == -1)
@@ -455,22 +453,14 @@ inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes, rb_tree_t *kroutes)
 
 #ifdef IPV4LL
        /* If there is no default route, see if we can use an IPv4LL one. */
-       memset(&def, 0, sizeof(def));
-       def.rt_dest.sa_family = AF_INET;
-       def.rt_netmask.sa_family = AF_INET;
-       have_default = (rb_tree_find_node(routes, &def) != NULL);
-       if (!have_default)
-               have_default = (rb_tree_find_node(kroutes, &def) != NULL);
-
-       if (!have_default) {
-               TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-                       if (ifp->active &&
-                           ipv4ll_defaultroute(routes, ifp) == 1)
-                               break;
-               }
+       if (have_default)
+               return true;
+
+       TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+               if (ifp->active &&
+                   ipv4ll_defaultroute(routes, ifp) == 1)
+                       break;
        }
-#else
-       UNUSED(kroutes);
 #endif
 
        return true;
index ac01b8944e03d56c457f4d0a489f1c7bf8758aa0..7cc06a52fe724207ad3960a73e7b1b775ad551bd 100644 (file)
@@ -118,7 +118,7 @@ int inet_cidrtoaddr(int, struct in_addr *);
 uint32_t ipv4_getnetmask(uint32_t);
 int ipv4_hasaddr(const struct interface *);
 
-bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *, rb_tree_t *);
+bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
 
 #define STATE_ADDED            0x01
 #define STATE_FAKE             0x02
index 772671322d0a0ec77bbd8f58d7ff9dd1bafe5fcc..8a1cc914a416095d14575d8335234a6064ac2cfe 100644 (file)
@@ -110,11 +110,7 @@ ipv4ll_subnetroute(rb_tree_t *routes, struct interface *ifp)
        in.s_addr = INADDR_ANY;
        sa_in_init(&rt->rt_gateway, &in);
        sa_in_init(&rt->rt_ifa, &state->addr->addr);
-       if (rb_tree_insert_node(routes, rt) != rt) {
-               rt_free(rt);
-               return 0;
-       }
-       return 1;
+       return rt_proto_add(routes, rt) ? 1 : 0;
 }
 
 int
@@ -137,11 +133,7 @@ ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp)
        sa_in_init(&rt->rt_netmask, &in);
        sa_in_init(&rt->rt_gateway, &in);
        sa_in_init(&rt->rt_ifa, &state->addr->addr);
-       if (rb_tree_insert_node(routes, rt) != rt) {
-               rt_free(rt);
-               return 0;
-       }
-       return 1;
+       return rt_proto_add(routes, rt) ? 1 : 0;
 }
 
 ssize_t
index e39a65843c5975f817e440c3a4ea02c68df6d1fb..f9ff14223c49271a2844f95dfec7cc518a56903a 100644 (file)
@@ -2226,8 +2226,8 @@ inet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
                            (IPV6_AF_ADDED | IPV6_AF_STATIC))
                        {
                                rt = inet6_makeprefix(ifp, NULL, ia);
-                               if (rt && rb_tree_insert_node(routes, rt) != rt)
-                                       rt_free(rt);
+                               if (rt)
+                                       rt_proto_add(routes, rt);
                        }
                }
        }
@@ -2251,17 +2251,14 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx, int expired,
                        rt = inet6_makeprefix(rap->iface, rap, addr);
                        if (rt) {
                                rt->rt_dflags |= RTDF_RA;
-                               if (rb_tree_insert_node(routes, rt) != rt)
-                                       rt_free(rt);
+                               rt_proto_add(routes, rt);
                        }
                }
                if (rap->lifetime) {
                        rt = inet6_makerouter(rap);
                        if (rt) {
                                rt->rt_dflags |= RTDF_RA;
-                               if (rb_tree_insert_node(routes, rt) != rt)
-                                       rt_free(rt);
-                               else if (have_default)
+                               if (rt_proto_add(routes, rt) && have_default)
                                        *have_default = true;
                        }
                }
@@ -2287,8 +2284,7 @@ inet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx,
                                if (rt == NULL)
                                        continue;
                                rt->rt_dflags |= RTDF_DHCP;
-                               if (rb_tree_insert_node(routes, rt) != rt)
-                                       rt_free(rt);
+                               rt_proto_add(routes, rt);
                        }
                }
        }
@@ -2313,7 +2309,7 @@ inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
 #ifdef DHCP6
        /* We have no way of knowing if prefixes added by DHCP are reachable
         * or not, so we have to assume they are.
-        * Add bound before delegated so we can prefer interfaces better */
+        * Add bound before delegated so we can prefer interfaces better. */
        if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)
                return false;
        if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)
index bcf477270866fd69cbf1118fc0a56afdd4fa1736..26c29e4699f9b2d9697c2d5de256c4a7c97502f8 100644 (file)
@@ -94,6 +94,17 @@ rt_maskedaddr(struct sockaddr *dst,
                memset(dstp, 0, (size_t)(addre - dstp));
 }
 
+int
+rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
+{
+       union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
+       union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
+
+       rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
+       rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
+       return sa_cmp(&ma1.sa, &ma2.sa);
+}
+
 /*
  * On some systems, host routes have no need for a netmask.
  * However DHCP specifies host routes using an all-ones netmask.
@@ -109,67 +120,63 @@ rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
        return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
 }
 
-static bool rt_compare_os;
-
 static int
-rt_compare(void *context, const void *node1, const void *node2)
+rt_compare_os(__unused void *context, const void *node1, const void *node2)
 {
        const struct rt *rt1 = node1, *rt2 = node2;
-       bool rt1u, rt2u;
-       union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
-       union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
        int c;
-       struct interface *ifp1, *ifp2;
-
-       /* Default routes come last. */
-       rt1u = !(rt1->rt_flags & RTF_HOST) &&
-           sa_is_unspecified(&rt1->rt_dest) &&
-           sa_is_unspecified(&rt1->rt_netmask);
-       rt2u = !(rt2->rt_flags & RTF_HOST) &&
-           sa_is_unspecified(&rt2->rt_dest) &&
-           sa_is_unspecified(&rt2->rt_netmask);
-       if (rt1u != rt2u)
-               return rt1u ? 1 : -1;
 
        /* Sort by masked destination. */
-       rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
-       rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
-       c = sa_cmp(&ma1.sa, &ma2.sa);
+       c = rt_cmp_dest(rt1, rt2);
        if (c != 0)
                return c;
 
-#ifndef HAVE_ROUTE_METRIC
-       if (context == &rt_compare_os)
-               return 0;
-#else
-       UNUSED(context);
+#ifdef HAVE_ROUTE_METRIC
+       c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
 #endif
+       return c;
+}
 
-       /* All other checks are by interface. */
-       if (rt1->rt_ifp == NULL || rt2->rt_ifp == NULL)
-               return 0;
+static int
+rt_compare_proto(__unused void *context, const void *node1, const void *node2)
+{
+       const struct rt *rt1 = node1, *rt2 = node2;
+       int c;
+       struct interface *ifp1, *ifp2;
+
+       assert(rt1->rt_ifp != NULL);
+       assert(rt2->rt_ifp != NULL);
        ifp1 = rt1->rt_ifp;
        ifp2 = rt2->rt_ifp;
 
-       /* Prefer interfaces with a carrier */
+       /* Prefer interfaces with a carrier. */
        c = ifp1->carrier - ifp2->carrier;
        if (c != 0)
                return -c;
 
-       /* Lower metric interfaces come first */
-       return (int)(ifp1->metric - ifp2->metric);
+       /* Lower metric interfaces come first. */
+       c = (int)(ifp1->metric - ifp2->metric);
+       if (c != 0)
+               return c;
+
+       /* Finally the order in which the route was given to us. */
+       if (rt1->rt_order > rt2->rt_order)
+               return 1;
+       if (rt1->rt_order < rt2->rt_order)
+               return -1;
+       return 0;
 }
 
 static const rb_tree_ops_t rt_compare_os_ops = {
-       .rbto_compare_nodes = rt_compare,
-       .rbto_compare_key = rt_compare,
+       .rbto_compare_nodes = rt_compare_os,
+       .rbto_compare_key = rt_compare_os,
        .rbto_node_offset = offsetof(struct rt, rt_tree),
-       .rbto_context = &rt_compare_os
+       .rbto_context = NULL
 };
 
-const rb_tree_ops_t rt_compare_list_ops = {
-       .rbto_compare_nodes = rt_compare,
-       .rbto_compare_key = rt_compare,
+const rb_tree_ops_t rt_compare_proto_ops = {
+       .rbto_compare_nodes = rt_compare_proto,
+       .rbto_compare_key = rt_compare_proto,
        .rbto_node_offset = offsetof(struct rt, rt_tree),
        .rbto_context = NULL
 };
@@ -200,6 +207,14 @@ rt_init(struct dhcpcd_ctx *ctx)
 #endif
 }
 
+bool
+rt_is_default(const struct rt *rt)
+{
+
+       return sa_is_unspecified(&rt->rt_dest) &&
+           sa_is_unspecified(&rt->rt_netmask);
+}
+
 static void
 rt_desc(const char *cmd, const struct rt *rt)
 {
@@ -224,9 +239,7 @@ rt_desc(const char *cmd, const struct rt *rt)
                else
                        loginfox("%s: %s host route to %s via %s",
                            ifname, cmd, dest, gateway);
-       } else if (sa_is_unspecified(&rt->rt_dest) &&
-                  sa_is_unspecified(&rt->rt_netmask))
-       {
+       } else if (rt_is_default(rt)) {
                if (gateway_unspec)
                        loginfox("%s: %s default route",
                            ifname, cmd);
@@ -351,6 +364,26 @@ rt_new(struct interface *ifp)
        return rt;
 }
 
+struct rt *
+rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
+{
+
+       rt->rt_order = ctx->rt_order++;
+       if (rb_tree_insert_node(tree, rt) == rt)
+               return rt;
+
+       rt_free(rt);
+       return NULL;
+}
+
+struct rt *
+rt_proto_add(rb_tree_t *tree, struct rt *rt)
+{
+
+       assert (rt->rt_ifp != NULL);
+       return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
+}
+
 void
 rt_free(struct rt *rt)
 {
@@ -629,15 +662,16 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
        struct rt *rt, *rtn;
        unsigned long long o;
 
-       rb_tree_init(&routes, &rt_compare_list_ops);
+       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_initrt(ctx, &kroutes, af);
+       ctx->rt_order = 0;
 
        switch (af) {
 #ifdef INET
        case AF_INET:
-               if (!inet_getroutes(ctx, &routes, &kroutes))
+               if (!inet_getroutes(ctx, &routes))
                        goto getfail;
                break;
 #endif
index 87a7c37d4cd616c6e72d4e94a7d1cde188c54886..70450100efe5c5c9b5e473a85c638e036cdb1612 100644 (file)
@@ -91,21 +91,26 @@ struct rt {
 #define        RTDF_RA                 0x08            /* Router Advertisement */
 #define        RTDF_DHCP               0x10            /* DHCP route */
 #define        RTDF_STATIC             0x20            /* Configured in dhcpcd */
+       size_t                  rt_order;
        rb_node_t               rt_tree;
 };
 
-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_dispose(struct dhcpcd_ctx *);
 void rt_free(struct rt *);
 void rt_freeif(struct interface *);
+bool rt_is_default(const struct rt *);
 void rt_headclear0(struct dhcpcd_ctx *, rb_tree_t *, int);
 void rt_headclear(rb_tree_t *, int);
 void rt_headfreeif(rb_tree_t *);
 struct rt * rt_new0(struct dhcpcd_ctx *);
 void rt_setif(struct rt *, struct interface *);
 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_build(struct dhcpcd_ctx *, int);