]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Remove remembering routes per interface and have a global routing table so we can...
authorRoy Marples <roy@marples.name>
Fri, 12 Sep 2008 18:08:07 +0000 (18:08 +0000)
committerRoy Marples <roy@marples.name>
Fri, 12 Sep 2008 18:08:07 +0000 (18:08 +0000)
configure.c
dhcpcd.h
if-bsd.c
if-linux.c
net.h

index c736ac2cb44aaaef2f1d6548f764a84267da8301..82663412047263954791f794776073ee2a525ccb 100644 (file)
 
 #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;
 }
index 19ca613f78d33eee7892f555e30e7b905871c4dd..4bfed04ffc155cf51b1c5538675329f018711eab 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -94,7 +94,6 @@ struct interface
 
        struct in_addr addr;
        struct in_addr net;
-       struct rt *routes;
 
        char leasefile[PATH_MAX];
        time_t start_uptime;
index 97197022d0310b8f1766d10849bcbd581066b0af..592cdc684a116b96225b3d2b493617b5b133f275 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -116,7 +116,7 @@ if_route(const struct interface *iface, const struct in_addr *destination,
        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 @@ if_route(const struct interface *iface, const struct in_addr *destination,
 
        /* 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 @@ if_route(const struct interface *iface, const struct in_addr *destination,
        }
 
        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;
index 3840f931ecd4c90213589bd4dc6355769631b159..4a9dd61bacc6247d40889460fcccc1d1281e75b5 100644 (file)
@@ -365,7 +365,10 @@ if_route(const struct interface *iface,
        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
diff --git a/net.h b/net.h
index 87d0d4c8bb0c243515ec67ede497971353a8761c..5191a7e7d90f5b79246b90891eb378399a4dad4b 100644 (file)
--- a/net.h
+++ b/net.h
 # 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;
 };