Mercurial > hg > dhcpcd
changeset 972:e61044370846 draft
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.
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Fri, 12 Sep 2008 18:08:07 +0000 |
| parents | 71c60fc4d1eb |
| children | 5b59480a3a9b |
| files | configure.c dhcpcd.h if-bsd.c if-linux.c net.h |
| diffstat | 5 files changed, 201 insertions(+), 123 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.c Thu Sep 11 22:55:27 2008 +0000 +++ b/configure.c Fri Sep 12 18:08:07 2008 +0000 @@ -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,8 +194,77 @@ 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; + + /* 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: adding route to %s/%d via %s", + iface->name, addr, + inet_ntocidr(rt->net), inet_ntoa(rt->gate)); + free(addr); + 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 +c_route(struct rt *ort, struct rt *nrt, const struct interface *iface) +{ + char *addr; + + /* 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; + + 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 +d_route(struct rt *rt, const struct interface *iface, int metric) { char *addr; int retval; @@ -199,119 +279,109 @@ return retval; } -static int -delete_routes(struct interface *iface) +static void +remove_routes(const struct interface *iface) { - struct rt *rt; - struct rt *rtn; - int retval = 0; + 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); - rt = iface->routes; - while (rt) { - rtn = rt->next; - retval += delete_route(iface, rt, iface->metric); - free(rt); - rt = rtn; + 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 + routes = trt->next; + free(trt); + } + free_routes(irts); } - iface->routes = NULL; - - return retval; -} - -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; - } - return -1; + free_routes(dor); + return; } -static int -configure_routes(struct interface *iface, const struct dhcp_message *dhcp) +static void +build_routes(void) { - 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; - - ort = get_option_routes(dhcp); - -#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; - } - - 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; - else - ort = rt; - } - } -#endif + struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl; + const struct interface *ifp; - /* 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); - - 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; + 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; } - 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; - } + 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 @@ 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 @@ 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 @@ 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 @@ 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; }
--- a/dhcpcd.h Thu Sep 11 22:55:27 2008 +0000 +++ b/dhcpcd.h Fri Sep 12 18:08:07 2008 +0000 @@ -94,7 +94,6 @@ struct in_addr addr; struct in_addr net; - struct rt *routes; char leasefile[PATH_MAX]; time_t start_uptime;
--- a/if-bsd.c Thu Sep 11 22:55:27 2008 +0000 +++ b/if-bsd.c Fri Sep 12 18:08:07 2008 +0000 @@ -116,7 +116,7 @@ struct rtm { struct rt_msghdr hdr; - char buffer[sizeof(su) * 3]; + char buffer[sizeof(su) * 5]; } rtm; char *bp = rtm.buffer; size_t l; @@ -140,6 +140,7 @@ /* This order is important */ rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + rtm.hdr.rtm_addrs |= RTA_IFP | RTA_IFA; #define ADDADDR(_addr) \ memset (&su, 0, sizeof(su)); \ @@ -172,6 +173,20 @@ } ADDADDR(netmask); + /* Make us a link layer socket for IFP */ + memset(&su, 0, sizeof(su)); + su.sdl.sdl_len = sizeof(su.sdl); + su.sdl.sdl_family = AF_LINK; + su.sdl.sdl_nlen = strlen(iface->name); + memcpy(&su.sdl.sdl_data, iface->name, (size_t)su.sdl.sdl_nlen); + su.sdl.sdl_alen = iface->hwlen; + memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen, + iface->hwaddr, (size_t)su.sdl.sdl_alen); + + l = SA_SIZE(&(su.sa)); + memcpy(bp, &su, l); + bp += l; + ADDADDR(&iface->addr); /* IFA */ #undef ADDADDR rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
--- a/if-linux.c Thu Sep 11 22:55:27 2008 +0000 +++ b/if-linux.c Fri Sep 12 18:08:07 2008 +0000 @@ -365,7 +365,10 @@ else { nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; /* We only change route metrics for kernel routes */ - nlm->rt.rtm_protocol = action ? RTPROT_BOOT : RTPROT_KERNEL; + if (action == 0 && netmask->s_addr == iface->net.s_addr) + nlm->rt.rtm_protocol = RTPROT_KERNEL; + else + nlm->rt.rtm_protocol = RTPROT_BOOT; if (gateway->s_addr == INADDR_ANY) nlm->rt.rtm_scope = RT_SCOPE_LINK; else
--- a/net.h Thu Sep 11 22:55:27 2008 +0000 +++ b/net.h Fri Sep 12 18:08:07 2008 +0000 @@ -79,18 +79,11 @@ # define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR) #endif -/* There is an argument that this should be converted to an STAIL using - * queue(3). However, that isn't readily available on all libc's that - * dhcpcd works on. The only benefit of STAILQ over this is the ability to - * quickly loop backwards through the list - currently we reverse the list - * and then move through it forwards. This isn't that much of a big deal - * though as the norm is to just have one default route, and an IPV4LL route. - * You can (and do) get more routes in the DHCP message, but not enough to - * really warrant a change to STAIL queue for performance reasons. */ struct rt { struct in_addr dest; struct in_addr net; struct in_addr gate; + const struct interface *iface; struct rt *next; };
