diff options
| author | Roy Marples <roy@marples.name> | 2008-09-12 18:08:07 +0000 |
|---|---|---|
| committer | Roy Marples <roy@marples.name> | 2008-09-12 18:08:07 +0000 |
| commit | 441f7d159dbb8448f075e177f9c3770503fc6aa4 (patch) | |
| tree | d15137305cff6bf6da5a7343f88691f17fa79859 /configure.c | |
| parent | be070f751ba8033aebb08e1cece0f0081d9c2a88 (diff) | |
| download | dhcpcd-441f7d159dbb8448f075e177f9c3770503fc6aa4.tar.xz | |
Remove remembering routes per interface and have a global routing table so we can change routes depending on interface state. This is very useful for the BSD's where there is no route metric.
Diffstat (limited to 'configure.c')
| -rw-r--r-- | configure.c | 284 |
1 files changed, 176 insertions, 108 deletions
diff --git a/configure.c b/configure.c index c736ac2c..82663412 100644 --- a/configure.c +++ b/configure.c @@ -49,6 +49,17 @@ #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" +#ifndef HAVE_ROUTE_METRIC +# ifdef __linux__ +# define HAVE_ROUTE_METRIC 1 +# endif +# ifndef HAVE_ROUTE_METRIC +# define HAVE_ROUTE_METRIC 0 +# endif +#endif + +static struct rt *routes = NULL; + static int exec_script(char *const *argv, char *const *env) { @@ -183,135 +194,194 @@ run_script(const struct interface *iface, const char *reason) return status; } +static struct rt * +find_route(struct rt *rts, const struct rt *r, struct rt **lrt, + const struct rt *srt) +{ + struct rt *rt; + + if (lrt) + *lrt = NULL; + for (rt = rts; rt; rt = rt->next) { + if (rt->dest.s_addr == r->dest.s_addr && +#if HAVE_ROUTE_METRIC + (srt || (!rt->iface || rt->iface->metric == r->iface->metric)) && +#endif + (!srt || srt != rt) && + rt->net.s_addr == r->net.s_addr) + return rt; + if (lrt) + *lrt = rt; + } + return NULL; +} + static int -delete_route(const struct interface *iface, struct rt *rt, int metric) +n_route(struct rt *rt, const struct interface *iface) { char *addr; - int retval; + + /* Don't set default routes if not asked to */ + if (rt->dest.s_addr == 0 && + rt->net.s_addr == 0 && + !(iface->state->options->options & DHCPCD_GATEWAY)) + return -1; addr = xstrdup(inet_ntoa(rt->dest)); - syslog(LOG_DEBUG, "%s: deleting route %s/%d via %s", iface->name, - addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); + syslog(LOG_DEBUG, "%s: adding route to %s/%d via %s", + iface->name, addr, + inet_ntocidr(rt->net), inet_ntoa(rt->gate)); free(addr); - retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric); - if (retval != 0 && errno != ENOENT && errno != ESRCH) - syslog(LOG_ERR," del_route: %m"); - return retval; + if (!add_route(iface, &rt->dest, &rt->net, &rt->gate, iface->metric)) + return 0; + if (errno != EEXIST) + syslog(LOG_ERR, "add_route: %m"); + return -1; } static int -delete_routes(struct interface *iface) +c_route(struct rt *ort, struct rt *nrt, const struct interface *iface) { - struct rt *rt; - struct rt *rtn; - int retval = 0; - - rt = iface->routes; - while (rt) { - rtn = rt->next; - retval += delete_route(iface, rt, iface->metric); - free(rt); - rt = rtn; - } - iface->routes = NULL; + char *addr; - return retval; -} + /* Don't set default routes if not asked to */ + if (nrt->dest.s_addr == 0 && + nrt->net.s_addr == 0 && + !(iface->state->options->options & DHCPCD_GATEWAY)) + return -1; -static int -in_routes(const struct rt *routes, const struct rt *rt) -{ - while (routes) { - if (routes->dest.s_addr == rt->dest.s_addr && - routes->net.s_addr == rt->net.s_addr && - routes->gate.s_addr == rt->gate.s_addr) - return 0; - routes = routes->next; - } + addr = xstrdup(inet_ntoa(nrt->dest)); + syslog(LOG_DEBUG, "%s: changing route to %s/%d via %s", + iface->name, addr, + inet_ntocidr(nrt->net), inet_ntoa(nrt->gate)); + free(addr); + del_route(ort->iface, &ort->dest, &ort->net, &ort->gate, ort->iface->metric); + if (!add_route(iface, &nrt->dest, &nrt->net, &nrt->gate, iface->metric)) + return 0; + syslog(LOG_ERR, "add_route: %m"); return -1; } + static int -configure_routes(struct interface *iface, const struct dhcp_message *dhcp) +d_route(struct rt *rt, const struct interface *iface, int metric) { - const struct if_options *ifo = iface->state->options; - struct rt *rt, *ort; - struct rt *rtn = NULL, *nr = NULL; - int remember; - int retval = 0; char *addr; + int retval; - ort = get_option_routes(dhcp); + addr = xstrdup(inet_ntoa(rt->dest)); + syslog(LOG_DEBUG, "%s: deleting route %s/%d via %s", iface->name, + addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate)); + free(addr); + retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric); + if (retval != 0 && errno != ENOENT && errno != ESRCH) + syslog(LOG_ERR," del_route: %m"); + return retval; +} -#ifdef IPV4LL_ALWAYSROUTE - if (ifo->options & DHCPCD_IPV4LL && - IN_PRIVATE(ntohl(dhcp->yiaddr))) - { - for (rt = ort; rt; rt = rt->next) { - /* Check if we have already got a link locale route - * dished out by the DHCP server */ - if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) && - rt->net.s_addr == htonl(LINKLOCAL_MASK)) - break; - rtn = rt; - } +static void +remove_routes(const struct interface *iface) +{ + struct rt *rt, *dor, *dnr = NULL, *irt, *lirt, *irts, *trt, *rtn, *lrt; + const struct interface *ifp; + + if (!iface->state->old) + return; + + if (iface->state->new) + dnr = get_option_routes(iface->state->new); - if (!rt) { - rt = xmalloc(sizeof(*rt)); - rt->dest.s_addr = htonl(LINKLOCAL_ADDR); - rt->net.s_addr = htonl(LINKLOCAL_MASK); - rt->gate.s_addr = 0; - rt->next = NULL; - if (rtn) - rtn->next = rt; + dor = get_option_routes(iface->state->old); + for (rt = dor; rt && (rtn = rt->next, 1); rt = rtn) { + rt->iface = iface; + /* Do we still have the route? */ + if (dnr && find_route(dnr, rt, NULL, NULL)) + continue; + /* Check if we manage the route */ + if (!(trt = find_route(routes, rt, &lrt, NULL))) + continue; + if (trt->iface != iface) + continue; + irt = NULL; + irts = NULL; + /* We may have an alternative route */ + if (!find_route(routes, rt, NULL, trt)) { + /* Do we have a replacement route? */ + for (ifp = ifaces; ifp; ifp = ifp->next) { + if (ifp == iface || !ifp->state->new) + continue; + irts = get_option_routes(ifp->state->new); + if ((irt = find_route(irts, rt, &lirt, NULL))) + break; + free_routes(irts); + irts = NULL; + } + } + if (irt) { + c_route(trt, irt, ifp); + trt->gate.s_addr = irt->gate.s_addr; + trt->iface = ifp; + } else { + d_route(trt, trt->iface, trt->iface->metric); + if (lrt) + lrt->next = trt->next; else - ort = rt; + routes = trt->next; + free(trt); } + free_routes(irts); } -#endif + free_routes(dor); + return; +} - /* Now remove old routes we no longer use. */ - for (rt = iface->routes; rt; rt = rt->next) - if (in_routes(ort, rt) != 0) - delete_route(iface, rt, iface->metric); +static void +build_routes(void) +{ + struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl; + const struct interface *ifp; - for (rt = ort; rt; rt = rt->next) { - /* Don't set default routes if not asked to */ - if (rt->dest.s_addr == 0 && - rt->net.s_addr == 0 && - !(ifo->options & DHCPCD_GATEWAY)) + for (ifp = ifaces; ifp; ifp = ifp->next) { + if (!ifp->state->new) continue; - - addr = xstrdup(inet_ntoa(rt->dest)); - syslog(LOG_DEBUG, "%s: adding route to %s/%d via %s", - iface->name, addr, - inet_ntocidr(rt->net), inet_ntoa(rt->gate)); - free(addr); - remember = add_route(iface, &rt->dest, - &rt->net, &rt->gate, iface->metric); - retval += remember; - - /* If we failed to add the route, we may have already added it - ourselves. If so, remember it again. */ - if (remember < 0) { - if (errno != EEXIST) - syslog(LOG_ERR, "add_route: %m"); - if (in_routes(iface->routes, rt) == 0) - remember = 1; - } - if (remember >= 0) { - rtn = xmalloc(sizeof(*rtn)); - rtn->dest.s_addr = rt->dest.s_addr; - rtn->net.s_addr = rt->net.s_addr; - rtn->gate.s_addr = rt->gate.s_addr; - rtn->next = nr; - nr = rtn; + dnr = get_option_routes(ifp->state->new); + for (rt = dnr; rt && (rtn = rt->next, 1); rt = rtn) { + rt->iface = ifp; + /* Is this route already in our table? */ + if ((find_route(nrs, rt, NULL, NULL))) + continue; + /* Do we already manage it? */ + if ((or = find_route(routes, rt, &rtl, NULL))) { + if (or->iface == ifp) { + if (rtl) + rtl->next = or->next; + else + routes = or->next; + rt = or; + } else { + if (c_route(or, rt, ifp) == 0) { + if (rtl) + rtl->next = or->next; + else + routes = or->next; + free(or); + } else + continue; + } + } else { + if (n_route(rt, ifp)) + continue; + } + if (dnr == rt) + dnr = rtn; + rt->iface = ifp; + rt->next = nrs; + nrs = rt; } + free_routes(dnr); } - free_routes(ort); - free_routes(iface->routes); - iface->routes = nr; - return retval; + free_routes(routes); + routes = nrs; } static int @@ -338,7 +408,7 @@ configure(struct interface *iface, const char *reason) struct in_addr addr; struct in_addr net; struct in_addr brd; -#ifdef __linux__ +#if HAVE_ROUTE_METRIC struct in_addr dest; struct in_addr gate; #endif @@ -357,14 +427,14 @@ configure(struct interface *iface, const char *reason) net.s_addr = get_netmask(addr.s_addr); if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1) brd.s_addr = addr.s_addr | ~net.s_addr; -#ifdef __linux__ +#if HAVE_ROUTE_METRIC dest.s_addr = addr.s_addr & net.s_addr; gate.s_addr = 0; #endif } else { /* Only reset things if we had set them before */ if (iface->addr.s_addr != 0) { - delete_routes(iface); + remove_routes(iface); delete_address(iface); } @@ -390,8 +460,8 @@ configure(struct interface *iface, const char *reason) iface->addr.s_addr != 0) delete_address(iface); -#ifdef __linux__ - /* On linux, we need to change the subnet route to have our metric. */ +#if HAVE_ROUTE_METRIC + /* We need to change the subnet route to have our metric. */ if (iface->metric > 0 && (net.s_addr != iface->net.s_addr || dest.s_addr != (iface->addr.s_addr & iface->net.s_addr))) @@ -405,12 +475,10 @@ configure(struct interface *iface, const char *reason) iface->addr.s_addr = addr.s_addr; iface->net.s_addr = net.s_addr; - configure_routes(iface, dhcp); - + build_routes(); if (!iface->state->lease.frominfo) if (write_lease(iface, dhcp) == -1) syslog(LOG_ERR, "write_lease: %m"); - run_script(iface, reason); return 0; } |
