From: Roy Marples Date: Fri, 7 Jun 2019 15:37:00 +0000 (+0100) Subject: route: Use order from message / config X-Git-Tag: v8.0.0~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=960abfd266971c705fa8c532df205d1b6aea1371;p=thirdparty%2Fdhcpcd.git route: Use order from message / config We can't just make the order up as that is prone to error. So apply ordering as from the source. --- diff --git a/src/dhcp.c b/src/dhcp.c index f548223e..23770073 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -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; diff --git a/src/dhcpcd.h b/src/dhcpcd.h index 539a4581..7ce3cad3 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -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; diff --git a/src/if-options.c b/src/if-options.c index 8628dd5c..e3d9c56e 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -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 */ diff --git a/src/ipv4.c b/src/ipv4.c index b63079bf..0cf5597f 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -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; diff --git a/src/ipv4.h b/src/ipv4.h index ac01b894..7cc06a52 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -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 diff --git a/src/ipv4ll.c b/src/ipv4ll.c index 77267132..8a1cc914 100644 --- a/src/ipv4ll.c +++ b/src/ipv4ll.c @@ -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 diff --git a/src/ipv6.c b/src/ipv6.c index e39a6584..f9ff1422 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -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) diff --git a/src/route.c b/src/route.c index bcf47727..26c29e46 100644 --- a/src/route.c +++ b/src/route.c @@ -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 diff --git a/src/route.h b/src/route.h index 87a7c37d..70450100 100644 --- a/src/route.h +++ b/src/route.h @@ -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);