]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Remove address family specific routing and introduce address family
authorRoy Marples <roy@marples.name>
Tue, 25 Oct 2016 15:33:02 +0000 (15:33 +0000)
committerRoy Marples <roy@marples.name>
Tue, 25 Oct 2016 15:33:02 +0000 (15:33 +0000)
agnostic routing.
This makes the platform drivers simpler and reduces some code duplication.
Needs a lot of testing.

24 files changed:
Makefile
dhcp.c
dhcp.h
dhcp6.c
dhcpcd.c
dhcpcd.h
if-bsd.c
if-linux.c
if-options.c
if-options.h
if-sun.c
if.c
if.h
ipv4.c
ipv4.h
ipv4ll.c
ipv4ll.h
ipv6.c
ipv6.h
ipv6nd.c
route.c [new file with mode: 0644]
route.h [new file with mode: 0644]
sa.c [new file with mode: 0644]
sa.h [new file with mode: 0644]

index 9d8257193ba7290ad2a3b402ea5bb6db400c71a8..0f01ce800170667fb59a638ce1a4d3e9f322972d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,8 @@
 
 PROG=          dhcpcd
 SRCS=          common.c control.c dhcpcd.c duid.c eloop.c
-SRCS+=         if.c if-options.c script.c
-SRCS+=         dhcp-common.c
+SRCS+=         if.c if-options.c sa.c route.c
+SRCS+=         dhcp-common.c script.c
 
 CFLAGS?=       -O2
 MKDIRS=
diff --git a/dhcp.c b/dhcp.c
index 3d1293b29e2443193e4752f404eb9911eb812fb1..0a475e169a9d8b001a9bb941d037471e10c6ed0b 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -63,6 +63,7 @@
 #include "if.h"
 #include "ipv4.h"
 #include "ipv4ll.h"
+#include "sa.h"
 #include "script.h"
 
 #define DAD            "Duplicate address detected"
@@ -394,58 +395,77 @@ decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl)
        return (ssize_t)bytes;
 }
 
-static struct rt_head *
-decode_rfc3442_rt(struct dhcpcd_ctx *ctx, const uint8_t *data, size_t dl)
+static int
+decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp,
+    const uint8_t *data, size_t dl, const struct bootp *bootp)
 {
        const uint8_t *p = data;
        const uint8_t *e;
        uint8_t cidr;
        size_t ocets;
-       struct rt_head *routes;
        struct rt *rt = NULL;
+       struct in_addr dest, netmask, gateway;
+       int n;
 
        /* Minimum is 5 -first is CIDR and a router length of 4 */
-       if (dl < 5)
-               return NULL;
+       if (dl < 5) {
+               errno = EINVAL;
+               return -1;
+       }
 
-       routes = malloc(sizeof(*routes));
-       TAILQ_INIT(routes);
+       n = 0;
        e = p + dl;
        while (p < e) {
                cidr = *p++;
                if (cidr > 32) {
-                       ipv4_freeroutes(routes);
                        errno = EINVAL;
-                       return NULL;
+                       return -1;
                }
 
                ocets = (size_t)(cidr + 7) / NBBY;
                if (p + 4 + ocets > e) {
-                       ipv4_freeroutes(routes);
                        errno = ERANGE;
-                       return NULL;
+                       return -1;
                }
 
-               rt = calloc(1, sizeof(*rt));
-               if (rt == NULL) {
-                       logger(ctx, LOG_ERR, "%s: %m", __func__);
-                       ipv4_freeroutes(routes);
-                       return NULL;
-               }
-               TAILQ_INSERT_TAIL(routes, rt, next);
+               if ((rt = rt_new(ifp)) == NULL)
+                       return -1;
+               TAILQ_INSERT_TAIL(routes, rt, rt_next);
 
                /* If we have ocets then we have a destination and netmask */
+               dest.s_addr = 0;
                if (ocets > 0) {
-                       memcpy(&rt->dest.s_addr, p, ocets);
+                       memcpy(&dest.s_addr, p, ocets);
                        p += ocets;
-                       rt->mask.s_addr = htonl(~0U << (32 - cidr));
-               }
+                       netmask.s_addr = htonl(~0U << (32 - cidr));
+               } else
+                       netmask.s_addr = 0;
 
                /* Finally, snag the router */
-               memcpy(&rt->gate.s_addr, p, 4);
+               memcpy(&gateway.s_addr, p, 4);
                p += 4;
+
+               /* A host route is normally set by having the
+                * gateway match the destination or assigned address */
+               if (gateway.s_addr == dest.s_addr ||
+                   (gateway.s_addr == bootp->yiaddr ||
+                   gateway.s_addr == bootp->ciaddr))
+               {
+                       gateway.s_addr = INADDR_ANY;
+                       netmask.s_addr = INADDR_BROADCAST;
+                       rt->rt_flags = RTF_HOST;
+               }
+
+               sa_in_init(&rt->rt_dest, &dest);
+               sa_in_init(&rt->rt_dest, &netmask);
+               sa_in_init(&rt->rt_gateway, &gateway);
+
+               /* If CIDR is 32 then it's a host route. */
+               if (cidr == 32)
+                       rt->rt_flags = RTF_HOST;
+               n++;
        }
-       return routes;
+       return n;
 }
 
 char *
@@ -554,17 +574,18 @@ route_netmask(uint32_t ip_in)
 /* We need to obey routing options.
  * If we have a CSR then we only use that.
  * Otherwise we add static routes and then routers. */
-static struct rt_head *
-get_option_routes(struct interface *ifp,
+static int
+get_option_routes(struct rt_head *routes, struct interface *ifp,
     const struct bootp *bootp, size_t bootp_len)
 {
        struct if_options *ifo = ifp->options;
        const uint8_t *p;
        const uint8_t *e;
-       struct rt_head *routes = NULL;
-       struct rt *route = NULL;
+       struct rt *rt = NULL;
+       struct in_addr dest, netmask, gateway;
        size_t len;
        const char *csr = "";
+       int n;
 
        /* If we have CSR's then we MUST use these only */
        if (!has_option_mask(ifo->nomask, DHO_CSR))
@@ -577,31 +598,23 @@ get_option_routes(struct interface *ifp,
                if (p)
                        csr = "MS ";
        }
-       if (p) {
-               routes = decode_rfc3442_rt(ifp->ctx, p, len);
-               if (routes) {
-                       const struct dhcp_state *state;
+       if (p && (n = decode_rfc3442_rt(routes, ifp, p, len, bootp)) != -1) {
+               const struct dhcp_state *state;
 
-                       state = D_CSTATE(ifp);
-                       if (!(ifo->options & DHCPCD_CSR_WARNED) &&
-                           !(state->added & STATE_FAKE))
-                       {
-                               logger(ifp->ctx, LOG_DEBUG,
-                                   "%s: using %sClassless Static Routes",
-                                   ifp->name, csr);
-                               ifo->options |= DHCPCD_CSR_WARNED;
-                       }
-                       return routes;
+               state = D_CSTATE(ifp);
+               if (!(ifo->options & DHCPCD_CSR_WARNED) &&
+                   !(state->added & STATE_FAKE))
+               {
+                       logger(ifp->ctx, LOG_DEBUG,
+                           "%s: using %sClassless Static Routes",
+                           ifp->name, csr);
+                       ifo->options |= DHCPCD_CSR_WARNED;
                }
+               return n;
        }
 
+       n = 0;
        /* OK, get our static routes first. */
-       routes = malloc(sizeof(*routes));
-       if (routes == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-               return NULL;
-       }
-       TAILQ_INIT(routes);
        if (!has_option_mask(ifo->nomask, DHO_STATICROUTE))
                p = get_option(ifp->ctx, bootp, bootp_len,
                    DHO_STATICROUTE, &len);
@@ -611,33 +624,33 @@ get_option_routes(struct interface *ifp,
        if (p && len % 8 == 0) {
                e = p + len;
                while (p < e) {
-                       if ((route = calloc(1, sizeof(*route))) == NULL) {
-                               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                               ipv4_freeroutes(routes);
-                               return NULL;
-                       }
-                       memcpy(&route->dest.s_addr, p, 4);
+                       memcpy(&dest.s_addr, p, sizeof(dest.s_addr));
                        p += 4;
-                       memcpy(&route->gate.s_addr, p, 4);
+                       memcpy(&gateway.s_addr, p, sizeof(gateway.s_addr));
                        p += 4;
                        /* RFC 2131 Section 5.8 states default route is
                         * illegal */
-                       if (route->dest.s_addr == htonl(INADDR_ANY)) {
-                               errno = EINVAL;
-                               free(route);
+                       if (sa_is_unspecified(&rt->rt_dest))
                                continue;
-                       }
+                       if ((rt = rt_new(ifp)) == NULL)
+                               return -1;
+
                        /* A host route is normally set by having the
                         * gateway match the destination or assigned address */
-                       if (route->gate.s_addr == route->dest.s_addr ||
-                           route->gate.s_addr == bootp->yiaddr)
+                       if (gateway.s_addr == dest.s_addr ||
+                            (gateway.s_addr == bootp->yiaddr ||
+                             gateway.s_addr == bootp->ciaddr))
                        {
-                               route->gate.s_addr = htonl(INADDR_ANY);
-                               route->mask.s_addr = htonl(INADDR_BROADCAST);
+                               gateway.s_addr = INADDR_ANY;
+                               netmask.s_addr = INADDR_BROADCAST;
+                               rt->rt_flags = RTF_HOST;
                        } else
-                               route->mask.s_addr =
-                                   route_netmask(route->dest.s_addr);
-                       TAILQ_INSERT_TAIL(routes, route, next);
+                               netmask.s_addr = route_netmask(dest.s_addr);
+                       sa_in_init(&rt->rt_dest, &dest);
+                       sa_in_init(&rt->rt_netmask, &netmask);
+                       sa_in_init(&rt->rt_gateway, &gateway);
+                       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+                       n++;
                }
        }
 
@@ -648,19 +661,22 @@ get_option_routes(struct interface *ifp,
                p = NULL;
        if (p) {
                e = p + len;
+               dest.s_addr = INADDR_ANY;
+               netmask.s_addr = INADDR_ANY;
                while (p < e) {
-                       if ((route = calloc(1, sizeof(*route))) == NULL) {
-                               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                               ipv4_freeroutes(routes);
-                               return NULL;
-                       }
-                       memcpy(&route->gate.s_addr, p, 4);
+                       if ((rt = rt_new(ifp)) == NULL)
+                               return -1;
+                       memcpy(&gateway.s_addr, p, sizeof(gateway.s_addr));
                        p += 4;
-                       TAILQ_INSERT_TAIL(routes, route, next);
+                       sa_in_init(&rt->rt_dest, &dest);
+                       sa_in_init(&rt->rt_netmask, &netmask);
+                       sa_in_init(&rt->rt_gateway, &gateway);
+                       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+                       n++;
                }
        }
 
-       return routes;
+       return n;
 }
 
 uint16_t
@@ -682,23 +698,14 @@ dhcp_get_mtu(const struct interface *ifp)
 
 /* Grab our routers from the DHCP message and apply any MTU value
  * the message contains */
-struct rt_head *
-dhcp_get_routes(struct interface *ifp)
+int
+dhcp_get_routes(struct rt_head *routes, struct interface *ifp)
 {
-       struct rt_head *routes;
-       uint16_t mtu;
        const struct dhcp_state *state;
 
-       state = D_CSTATE(ifp);
-       routes = get_option_routes(ifp, state->new, state->new_len);
-       if ((mtu = dhcp_get_mtu(ifp)) != 0) {
-               struct rt *rt;
-
-               TAILQ_FOREACH(rt, routes, next) {
-                       rt->mtu = mtu;
-               }
-       }
-       return routes;
+       if ((state = D_CSTATE(ifp)) == NULL || state->state != DHS_BOUND)
+               return 0;
+       return get_option_routes(routes, ifp, state->new, state->new_len);
 }
 
 /* Assumes DHCP options */
@@ -2621,6 +2628,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
 #endif
        dhcp_close(ifp);
 
+       state->state = DHS_INIT;
        free(state->offer);
        state->offer = NULL;
        state->offer_len = 0;
@@ -3586,7 +3594,7 @@ dhcp_start1(void *arg)
                                state->new_len = state->offer_len;
                                state->addr = ia;
                                state->added |= STATE_ADDED | STATE_FAKE;
-                               ipv4_buildroutes(ifp->ctx);
+                               rt_build(ifp->ctx, AF_INET);
                        } else
                                logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
                }
@@ -3771,7 +3779,7 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia)
                                dhcp_message_add_addr(state->new, i, ia->brd);
        }
        state->reason = "STATIC";
-       ipv4_buildroutes(ifp->ctx);
+       rt_build(ifp->ctx, AF_INET);
        script_runreason(ifp, state->reason);
        if (ifo->options & DHCPCD_INFORM) {
                state->state = DHS_INFORM;
diff --git a/dhcp.h b/dhcp.h
index a21eea30ae26bb70ae5ccc52a37ee891a7e2a9bb..931fa377d834d5c152c5af4725c1cfa0b2736db1 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -235,7 +235,7 @@ ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t);
 void dhcp_printoptions(const struct dhcpcd_ctx *,
     const struct dhcp_opt *, size_t);
 uint16_t dhcp_get_mtu(const struct interface *);
-struct rt_head *dhcp_get_routes(struct interface *);
+int dhcp_get_routes(struct rt_head *, struct interface *);
 ssize_t dhcp_env(char **, const char *, const struct bootp *, size_t,
     const struct interface *);
 
diff --git a/dhcp6.c b/dhcp6.c
index 389263cdb12418d4ce50786ce0e0f98cb06e5efa..475d546873402500499b80ad0cf1c6f6aa9bdf36 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1000,7 +1000,7 @@ dhcp6_freedrop_addrs(struct interface *ifp, int drop,
        if (state) {
                ipv6_freedrop_addrs(&state->addrs, drop, ifd);
                if (drop)
-                       ipv6_buildroutes(ifp->ctx);
+                       rt_build(ifp->ctx, AF_INET6);
        }
 }
 
@@ -2653,8 +2653,8 @@ dhcp6_delegate_prefix(struct interface *ifp)
                if (k && !carrier_warned) {
                        ifd_state = D6_STATE(ifd);
                        ipv6_addaddrs(&ifd_state->addrs);
-                       if_initrt6(ifd->ctx);
-                       ipv6_buildroutes(ifd->ctx);
+                       if_initrt(ifd->ctx);
+                       rt_build(ifd->ctx, AF_INET6);
                        dhcp6_script_try_run(ifd, 1);
                }
        }
@@ -2720,8 +2720,8 @@ dhcp6_find_delegates(struct interface *ifp)
                state = D6_STATE(ifp);
                state->state = DH6S_DELEGATED;
                ipv6_addaddrs(&state->addrs);
-               if_initrt6(ifp->ctx);
-               ipv6_buildroutes(ifp->ctx);
+               if_initrt(ifp->ctx);
+               rt_build(ifp->ctx, AF_INET6);
                dhcp6_script_try_run(ifp, 1);
        }
        return k;
@@ -3236,8 +3236,8 @@ dhcp6_handledata(void *arg)
                else if (state->expire == 0)
                        logger(ifp->ctx, has_new ? LOG_INFO : LOG_DEBUG,
                            "%s: will expire", ifp->name);
-               if_initrt6(ifp->ctx);
-               ipv6_buildroutes(ifp->ctx);
+               if_initrt(ifp->ctx);
+               rt_build(ifp->ctx, AF_INET6);
                dhcp6_writelease(ifp);
 #ifndef SMALL
                dhcp6_delegate_prefix(ifp);
index 33dfe05ee84dc279014e4a88732f02ea3a29f71e..e5facde4ab8d8be69ff84bffd44906f357d4a667 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -649,13 +649,9 @@ dhcpcd_initstate2(struct interface *ifp, unsigned long long options)
        } else
                ifo = ifp->options;
 
-       if (ifo->options & DHCPCD_IPV4 && ipv4_init(ifp->ctx) == -1) {
-               logger(ifp->ctx, LOG_ERR, "ipv4_init: %m");
-               ifo->options &= ~DHCPCD_IPV4;
-       }
        if (ifo->options & DHCPCD_IPV6 && ipv6_init(ifp->ctx) == NULL) {
                logger(ifp->ctx, LOG_ERR, "ipv6_init: %m");
-               ifo->options &= ~DHCPCD_IPV6RS;
+               ifo->options &= ~DHCPCD_IPV6;
        }
 }
 
@@ -1885,6 +1881,8 @@ printpidfile:
                }
        }
 
+       rt_init(&ctx);
+
        TAILQ_FOREACH(ifp, ctx.ifaces, next) {
                if (ifp->active)
                        dhcpcd_initstate1(ifp, argc, argv, 0);
@@ -1958,6 +1956,8 @@ exit_failure:
        i = EXIT_FAILURE;
 
 exit1:
+       if (control_stop(&ctx) == -1)
+               logger(&ctx, LOG_ERR, "control_stop: %m:");
        /* Free memory and close fd's */
        if (ctx.ifaces) {
                while ((ifp = TAILQ_FIRST(ctx.ifaces))) {
@@ -1966,6 +1966,7 @@ exit1:
                }
                free(ctx.ifaces);
        }
+       rt_dispose(&ctx);
        free(ctx.duid);
        if (ctx.link_fd != -1) {
                eloop_event_delete(ctx.eloop, ctx.link_fd);
@@ -1974,11 +1975,8 @@ exit1:
        if_closesockets(&ctx);
        free_options(ifo);
        free_globals(&ctx);
-       ipv4_ctxfree(&ctx);
        ipv6_ctxfree(&ctx);
        dev_stop(&ctx);
-       if (control_stop(&ctx) == -1)
-               logger(&ctx, LOG_ERR, "control_stop: %m:");
        eloop_free(ctx.eloop);
        free(ctx.iov[0].iov_base);
 
index a41360d150024146f0bb08786a4ba1f69bf816c3..aa1ede6a941418f5ab35164cc4e1acb6f0069dc9 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -113,6 +113,10 @@ struct dhcpcd_ctx {
        size_t duid_len;
        struct if_head *ifaces;
 
+       struct rt_head routes;  /* our routes */
+       struct rt_head kroutes; /* all kernel routes */
+       struct rt_head froutes; /* free routes for re-use */
+
        int pf_inet_fd;
 #ifdef IFLR_ACTIVE
        int pf_link_fd;
@@ -150,8 +154,6 @@ struct dhcpcd_ctx {
 #ifdef INET
        struct dhcp_opt *dhcp_opts;
        size_t dhcp_opts_len;
-       struct rt_head *ipv4_routes;
-       struct rt_head *ipv4_kroutes;
 
        int udp_fd;
 
index 51aa8449c50a216796a6211334e44daa895aadce..40df9a37217cf5f3925bf660201efe17f8e30be6 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -1,5 +1,5 @@
 /*
- * dhcpcd - DHCP client daemon
+ * BSD interface driver for dhcpcd
  * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
  * All rights reserved
 
@@ -57,6 +57,7 @@
 #  include <net80211/ieee80211_ioctl.h>
 #endif
 
+#include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fnmatch.h>
@@ -82,6 +83,8 @@
 #include "ipv4ll.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
+#include "route.h"
+#include "sa.h"
 
 #include "bpf-filter.h"
 
 #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
 #endif
 
-#define COPYOUT(sin, sa) do {                                                \
-       if ((sa) && ((sa)->sa_family == AF_INET || (sa)->sa_family == 255))   \
-               (sin) = ((const struct sockaddr_in *)(const void *)           \
-                   (sa))->sin_addr;          \
-       } while (0)
-
-#define COPYOUT6(sin, sa) do {                                               \
-       if ((sa) && ((sa)->sa_family == AF_INET6 || (sa)->sa_family == 255))  \
-               (sin) = ((const struct sockaddr_in6 *)(const void *)          \
-                   (sa))->sin6_addr;     \
-       } while (0)
-
-#ifndef CLLADDR
-#  define CLLADDR(s) (const void *)((s)->sdl_data + (s)->sdl_nlen)
+#ifdef INET6
+static void
+ifa_scope(struct sockaddr_in6 *, unsigned int);
 #endif
 
 struct priv {
@@ -359,29 +351,44 @@ if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
        return NULL;
 }
 
-static int
-if_rtmsg(unsigned char cmd, const struct interface *ifp,
-    int addrs, int flags,
-#ifdef RTP_CONNECTED
-    int priority,
+static void
+if_copysa(struct sockaddr *dst, const struct sockaddr *src)
+{
+
+       memcpy(dst, src, src->sa_len);
+#ifdef __KAME__
+       if (dst->sa_family == AF_INET6) {
+               struct in6_addr *in6;
+
+               in6 = &satosin6(dst)->sin6_addr;
+               if (IN6_IS_ADDR_LINKLOCAL(in6))
+                       in6->s6_addr[2] = in6->s6_addr[3] = '\0';
+       }
 #endif
-    const struct sockaddr *dst, const struct sockaddr *mask,
-    const struct sockaddr *gate, const struct sockaddr *src,
-    uint32_t mtu)
+}
+
+int
+if_route(unsigned char cmd, const struct rt *rt)
 {
+       struct dhcpcd_ctx *ctx;
        struct rtm
        {
                struct rt_msghdr hdr;
                char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
-       } rtm;
-       char *bp = rtm.buffer;
+       } rtmsg;
+       struct rt_msghdr *rtm = &rtmsg.hdr;
+       char *bp = rtmsg.buffer;
        size_t l;
        struct sockaddr_dl sdl;
+       bool gateway_unspec;
+
+       assert(rt != NULL);
+       ctx = rt->rt_ifp->ctx;
 
        if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) &&
-           ifp->ctx->options & DHCPCD_DAEMONISE &&
-           !(ifp->ctx->options & DHCPCD_DAEMONISED))
-               ifp->ctx->options |= DHCPCD_RTM_PPID;
+           ctx->options & DHCPCD_DAEMONISE &&
+           !(ctx->options & DHCPCD_DAEMONISED))
+               ctx->options |= DHCPCD_RTM_PPID;
 
 #define ADDSA(sa) do {                                                       \
                l = RT_ROUNDUP(((sa)->sa_len));                               \
@@ -389,62 +396,213 @@ if_rtmsg(unsigned char cmd, const struct interface *ifp,
                bp += l;                                                      \
        }  while (0 /* CONSTCOND */)
 
-       memset(&rtm, 0, sizeof(rtm));
-       rtm.hdr.rtm_version = RTM_VERSION;
-       rtm.hdr.rtm_type = cmd;
+       memset(rtm, 0, sizeof(*rtm));
+       rtm->rtm_version = RTM_VERSION;
+       rtm->rtm_type = cmd;
 #ifdef __OpenBSD__
-       rtm.hdr.rtm_pid = getpid();
+       rtm->rtm_pid = getpid();
 #endif
-       rtm.hdr.rtm_seq = ++ifp->ctx->seq;
-       rtm.hdr.rtm_flags = flags;
-       rtm.hdr.rtm_addrs = RTA_DST | addrs;
+       rtm->rtm_seq = ++ctx->seq;
+       rtm->rtm_flags = (int)rt->rt_flags;
+       rtm->rtm_addrs = RTA_DST;
 #ifdef RTF_PINNED
        if (cmd != RTM_ADD)
-               rtm.hdr.rtm_flags |= RTF_PINNED;
+               rtm->rtm_flags |= RTF_PINNED;
+#endif
+
+       gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
+
+       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
+               bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
+
+               rtm->rtm_flags |= RTF_UP;
+               rtm->rtm_addrs |= RTA_GATEWAY | RTA_IFP;
+               if (!(rtm->rtm_flags & RTF_REJECT) &&
+                   !sa_is_loopback(&rt->rt_gateway))
+               {
+                       rtm->rtm_addrs |= RTA_IFP;
+                       if (!sa_is_unspecified(&rt->rt_ifa))
+                               rtm->rtm_addrs |= RTA_IFA;
+               }
+               if (netmask_bcast)
+                       rtm->rtm_flags |= RTF_HOST;
+               /* Network routes are cloning or connected if supported.
+                * All other routes are static. */
+               if (gateway_unspec) {
+#ifdef RTF_CLONING
+                       rtm->rtm_flags |= RTF_CLONING;
+#endif
+#ifdef RTF_CONNECTED
+                       rtm->rtm_flags |= RTF_CONNECTED;
 #endif
 #ifdef RTP_CONNECTED
-       rtm.hdr.rtm_priority = priority;
+                       rtm->rtm_priority = RTP_CONNECTED;
 #endif
+#ifdef RTF_CLONING
+                       if (netmask_bcast) {
+                               /*
+                                * We add a cloning network route for a single
+                                * host. Traffic to the host will generate a
+                                * cloned route and the hardware address will
+                                * resolve correctly.
+                                * It might be more correct to use RTF_HOST
+                                * instead of RTF_CLONING, and that does work,
+                                * but some OS generate an arp warning
+                                * diagnostic which we don't want to do.
+                                */
+                               rtm->rtm_flags &= ~RTF_HOST;
+                       }
+#endif
+               } else
+                       rtm->rtm_flags |= RTF_GATEWAY;
 
-       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
-               rtm.hdr.rtm_flags |= RTF_UP;
-               rtm.hdr.rtm_addrs |= RTA_GATEWAY;
-               if (rtm.hdr.rtm_flags & (RTF_GATEWAY | RTF_REJECT))
-                       /* Going via lo0 so remove the interface flags. */
-                       rtm.hdr.rtm_addrs &= ~(RTA_IFP | RTA_IFA);
-               if (mtu != 0) {
-                       rtm.hdr.rtm_inits |= RTV_MTU;
-                       rtm.hdr.rtm_rmx.rmx_mtu = mtu;
+               /* Emulate the kernel by marking address generated
+                * network routes non-static. */
+               if (!(rt->rt_dflags & RTDF_IFA_ROUTE))
+                       rtm->rtm_flags |= RTF_STATIC;
+
+               if (rt->rt_mtu != 0) {
+                       rtm->rtm_inits |= RTV_MTU;
+                       rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
                }
        }
 
-       ADDSA(dst);
-       if_linkaddr(&sdl, ifp);
-       if (rtm.hdr.rtm_addrs & RTA_GATEWAY) {
-               if (gate == NULL)
+       if (!(rtm->rtm_flags & RTF_HOST))
+               rtm->rtm_addrs |= RTA_NETMASK;
+
+       if_linkaddr(&sdl, rt->rt_ifp);
+
+       ADDSA(&rt->rt_dest);
+
+       if (rtm->rtm_addrs & RTA_GATEWAY) {
+               if (gateway_unspec)
                        ADDSA((struct sockaddr *)&sdl);
-               else
-                       ADDSA(gate);
+               else {
+                       union sa_ss gateway;
+
+                       if_copysa(&gateway.sa, &rt->rt_gateway);
+#ifdef INET6
+                       if (gateway.sa.sa_family == AF_INET6)
+                               ifa_scope(&gateway.sin6, rt->rt_ifp->index);
+#endif
+                       ADDSA(&gateway.sa);
+               }
        }
-       if (rtm.hdr.rtm_addrs & RTA_NETMASK)
-               ADDSA(mask);
 
-       if (rtm.hdr.rtm_addrs & RTA_IFP) {
-               rtm.hdr.rtm_index = (unsigned short)ifp->index;
+       if (rtm->rtm_addrs & RTA_NETMASK)
+               ADDSA(&rt->rt_netmask);
+
+       if (rtm->rtm_addrs & RTA_IFP) {
+               rtm->rtm_index = (unsigned short)rt->rt_ifp->index;
                ADDSA((struct sockaddr *)&sdl);
        }
 
-       if (rtm.hdr.rtm_addrs & RTA_IFA)
-               ADDSA(src);
+       if (rtm->rtm_addrs & RTA_IFA)
+               ADDSA(&rt->rt_ifa);
 
 #undef ADDSA
 
-       rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
-       if (write(ifp->ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
+       rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
+       if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1)
                return -1;
-       ifp->ctx->sseq = ifp->ctx->seq;
+       ctx->sseq = ctx->seq;
        return 0;
 }
+
+static int
+if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
+{
+       const struct sockaddr *rti_info[RTAX_MAX];
+
+       if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
+               return -1;
+#ifdef RTF_CLONED
+       if (rtm->rtm_flags & RTF_CLONED)
+               return -1;
+#endif
+#ifdef RTF_LOCAL
+       if (rtm->rtm_flags & RTF_LOCAL)
+               return -1;
+#endif
+#ifdef RTF_BROADCAST
+       if (rtm->rtm_flags & RTF_BROADCAST)
+               return -1;
+#endif
+
+       get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
+       memset(rt, 0, sizeof(*rt));
+
+       rt->rt_flags = (unsigned int)rtm->rtm_flags;
+       if_copysa(&rt->rt_dest, rti_info[RTAX_DST]);
+       if (rtm->rtm_addrs & RTA_NETMASK) {
+               if_copysa(&rt->rt_netmask, rti_info[RTAX_NETMASK]);
+               if (rt->rt_netmask.sa_family == 255) /* Why? */
+                       rt->rt_netmask.sa_family = rt->rt_dest.sa_family;
+       }
+       /* dhcpcd likes an unspecified gateway to indicate via the link. */
+       if (rt->rt_flags & RTF_GATEWAY &&
+           rti_info[RTAX_GATEWAY]->sa_family != AF_LINK)
+               if_copysa(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
+       if_copysa(&rt->rt_ifa, rti_info[RTAX_IFA]);
+       rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
+
+       if (rtm->rtm_index)
+               rt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
+       else if (rtm->rtm_addrs & RTA_IFP)
+               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
+       else if (rtm->rtm_addrs & RTA_GATEWAY)
+               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
+       else
+               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);
+
+       if (rt->rt_ifp == NULL) {
+               errno = ESRCH;
+               return -1;
+       }
+       return 0;
+}
+
+int
+if_initrt(struct dhcpcd_ctx *ctx)
+{
+       struct rt_msghdr *rtm;
+       int mib[6];
+       size_t needed;
+       char *buf, *p, *end;
+       struct rt rt;
+
+       rt_headclear(&ctx->kroutes);
+
+       mib[0] = CTL_NET;
+       mib[1] = PF_ROUTE;
+       mib[2] = 0;
+       mib[3] = AF_UNSPEC;
+       mib[4] = NET_RT_DUMP;
+       mib[5] = 0;
+
+       if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+               return -1;
+       if (needed == 0)
+               return 0;
+       if ((buf = malloc(needed)) == NULL)
+               return -1;
+       if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) {
+               free(buf);
+               return -1;
+       }
+
+       end = buf + needed;
+       for (p = buf; p < end; p += rtm->rtm_msglen) {
+               rtm = (void *)p;
+               if (if_copyrt(ctx, &rt, rtm) == 0) {
+                       rt.rt_dflags |= RTDF_INIT;
+                       rt_recvrt(RTM_ADD, &rt);
+               }
+       }
+       free(buf);
+       return 0;
+}
+
 #endif
 
 #ifdef INET
@@ -647,207 +805,7 @@ if_address(unsigned char cmd, const struct ipv4_addr *ia)
        return r;
 }
 
-static int
-if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
-{
-       const struct sockaddr *sa, *rti_info[RTAX_MAX];
-
-       sa = (const void *)(rtm + 1);
-       if (sa->sa_family != AF_INET)
-               return -1;
-       if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
-               return -1;
-#ifdef RTF_CLONED
-       if (rtm->rtm_flags & RTF_CLONED)
-               return -1;
-#endif
-#ifdef RTF_LOCAL
-       if (rtm->rtm_flags & RTF_LOCAL)
-               return -1;
-#endif
-#ifdef RTF_BROADCAST
-       if (rtm->rtm_flags & RTF_BROADCAST)
-               return -1;
-#endif
-
-       get_addrs(rtm->rtm_addrs, sa, rti_info);
-       memset(rt, 0, sizeof(*rt));
-       rt->flags = (unsigned int)rtm->rtm_flags;
-       COPYOUT(rt->dest, rti_info[RTAX_DST]);
-       if (rtm->rtm_addrs & RTA_NETMASK)
-               COPYOUT(rt->mask, rti_info[RTAX_NETMASK]);
-       else
-               rt->mask.s_addr = INADDR_BROADCAST;
-       COPYOUT(rt->gate, rti_info[RTAX_GATEWAY]);
-       COPYOUT(rt->src, rti_info[RTAX_IFA]);
-       rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
-
-       if (rtm->rtm_index)
-               rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index);
-       else if (rtm->rtm_addrs & RTA_IFP)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_IFP]);
-       else if (rtm->rtm_addrs & RTA_GATEWAY)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
-
-       /* If we don't have an interface and it's a host route, it maybe
-        * to a local ip via the loopback interface. */
-       if (rt->iface == NULL &&
-           !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
-       {
-               struct ipv4_addr *ia;
-
-               if ((ia = ipv4_findaddr(ctx, &rt->dest)))
-                       rt->iface = ia->iface;
-       }
-
-       if (rt->iface == NULL) {
-               errno = ESRCH;
-               return -1;
-       }
-       return 0;
-}
-
-int
-if_route(unsigned char cmd, const struct rt *rt)
-{
-       struct sockaddr_in dst = {
-               .sin_family = AF_INET,
-               .sin_len = sizeof(dst),
-               .sin_addr = rt->dest
-       };
-       struct sockaddr_in mask = {
-               .sin_family = AF_INET,
-               .sin_len = sizeof(mask),
-               .sin_addr = rt->mask
-       };
-       struct sockaddr_in gate = {
-               .sin_family = AF_INET,
-               .sin_len = sizeof(gate),
-               .sin_addr = rt->gate
-       };
-       struct sockaddr_in src = {
-               .sin_family = AF_INET,
-               .sin_len = sizeof(src),
-               .sin_addr = rt->src
-       };
-       struct sockaddr_in *g;
-       int addrs, flags;
-#ifdef RTP_CONNECTED
-       int priority = 0;
-#endif
-
-       addrs = 0;
-       flags = (int)rt->flags;
-
-       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
-               addrs |= RTA_GATEWAY | RTA_IFP;
-               /* Subnet routes are cloning or connected if supported.
-                * All other routes are static. */
-               if (rt->gate.s_addr == ntohl(INADDR_ANY)) {
-#ifdef RTF_CLONING
-                       flags |= RTF_CLONING;
-#endif
-#ifdef RTF_CONNECTED
-                       flags |= RTF_CONNECTED;
-#endif
-#ifdef RTP_CONNECTED
-                       priority = RTP_CONNECTED;
-#endif
-               } else
-                       flags |= RTF_STATIC;
-               if (rt->src.s_addr != ntohl(INADDR_ANY))
-                       addrs |= RTA_IFA;
-       }
-
-       if (rt->mask.s_addr == htonl(INADDR_BROADCAST) &&
-           rt->gate.s_addr == htonl(INADDR_ANY))
-       {
-#ifdef RTF_CLONING
-               /* We add a cloning network route for a single host.
-                * Traffic to the host will generate a cloned route and the
-                * hardware address will resolve correctly.
-                * It might be more correct to use RTF_HOST instead of
-                * RTF_CLONING, and that does work, but some OS generate
-                * an arp warning diagnostic which we don't want to do. */
-               flags |= RTF_CLONING;
-               addrs |= RTA_NETMASK;
-#else
-               flags |= RTF_HOST;
-#endif
-       } else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) &&
-           rt->mask.s_addr == htonl(INADDR_BROADCAST))
-       {
-               flags |= RTF_HOST | RTF_GATEWAY;
-               /* Going via lo0 so remove the interface flags */
-               addrs &= ~(RTA_IFA | RTA_IFP);
-       } else {
-               addrs |= RTA_NETMASK;
-               if (flags & RTF_STATIC)
-                       flags |= RTF_GATEWAY;
-               if (rt->mask.s_addr == htonl(INADDR_BROADCAST))
-                       flags |= RTF_HOST;
-       }
-
-       if ((flags & RTF_HOST && rt->gate.s_addr == htonl(INADDR_ANY)) ||
-#ifdef RTF_CLONING
-          flags & RTF_CLONING ||
-#endif
-#ifdef RTF_CONNECTED
-           flags & RTF_CONNECTED ||
-#endif
-#ifdef RTP_CONNECTED
-           priority != 0 ||
-#endif
-           !(flags & RTF_STATIC))
-               g = NULL;
-       else
-               g = &gate;
-       return if_rtmsg(cmd, rt->iface, addrs, flags,
-#ifdef RTP_CONNECTED
-           priority,
-#endif
-           (struct sockaddr *)&dst, (struct sockaddr *)&mask,
-           (struct sockaddr *)g, (struct sockaddr *)&src, rt->mtu);
-}
-
-int
-if_initrt(struct dhcpcd_ctx *ctx)
-{
-       struct rt_msghdr *rtm;
-       int mib[6];
-       size_t needed;
-       char *buf, *p, *end;
-       struct rt rt;
-
-       ipv4_freerts(ctx->ipv4_kroutes);
 
-       mib[0] = CTL_NET;
-       mib[1] = PF_ROUTE;
-       mib[2] = 0;
-       mib[3] = AF_INET;
-       mib[4] = NET_RT_DUMP;
-       mib[5] = 0;
-
-       if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
-               return -1;
-       if (needed == 0)
-               return 0;
-       if ((buf = malloc(needed)) == NULL)
-               return -1;
-       if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) {
-               free(buf);
-               return -1;
-       }
-
-       end = buf + needed;
-       for (p = buf; p < end; p += rtm->rtm_msglen) {
-               rtm = (void *)p;
-               if (if_copyrt(ctx, &rt, rtm) == 0)
-                       ipv4_handlert(ctx, RTM_ADD, &rt, 1);
-       }
-       free(buf);
-       return 0;
-}
 
 #if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS))
 int
@@ -897,15 +855,6 @@ ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex)
 #endif
 }
 
-#ifdef __KAME__
-#define DESCOPE(ia6) do {                                                    \
-       if (IN6_IS_ADDR_LINKLOCAL((ia6)))                                     \
-               (ia6)->s6_addr[2] = (ia6)->s6_addr[3] = '\0';                 \
-       } while (/*CONSTCOND */0)
-#else
-#define DESCOPE(ia6)
-#endif
-
 int
 if_address6(unsigned char cmd, const struct ipv6_addr *ia)
 {
@@ -1006,217 +955,6 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia)
            cmd == RTM_DELADDR ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa);
 }
 
-static int
-if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, const struct rt_msghdr *rtm)
-{
-       const struct sockaddr *sa, *rti_info[RTAX_MAX];
-
-       sa = (const void *)(rtm + 1);
-       if (sa->sa_family != AF_INET6)
-               return -1;
-       if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
-               return -1;
-#ifdef RTF_CLONED
-       if (rtm->rtm_flags & (RTF_CLONED | RTF_HOST))
-               return -1;
-#else
-       if (rtm->rtm_flags & RTF_HOST)
-               return -1;
-#endif
-#ifdef RTF_LOCAL
-       if (rtm->rtm_flags & RTF_LOCAL)
-               return -1;
-#endif
-
-       get_addrs(rtm->rtm_addrs, sa, rti_info);
-       memset(rt, 0, sizeof(*rt));
-       rt->flags = (unsigned int)rtm->rtm_flags;
-       COPYOUT6(rt->dest, rti_info[RTAX_DST]);
-       DESCOPE(&rt->dest);
-       if (rtm->rtm_addrs & RTA_NETMASK) {
-               /*
-                * We need to zero out the struct beyond sin6_len and
-                * ensure it's valid.
-                * I have no idea what the invalid data is for, could be
-                * a kernel bug or actually used for something.
-                * Either way it needs to be zeroed out.
-                */
-               const struct sockaddr_in6 *sin6;
-               size_t e, i, final = 0, illegal = 0;
-               const unsigned char *p;
-
-               sin6 = (const void *)rti_info[RTAX_NETMASK];
-               rt->mask = sin6->sin6_addr;
-               e = sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr);
-               if (e > sizeof(struct in6_addr))
-                       e = sizeof(struct in6_addr);
-               for (p = (const unsigned char *)&sin6->sin6_addr, i = 0;
-                   i < e;
-                   p++)
-               {
-                       if (final && *p) {
-                               illegal = 1;
-                               rt->mask.s6_addr[i++] = 0x00;
-                               continue;
-                       }
-                       switch (*p & 0xff) {
-                       case 0xff:
-                               break;
-                       case 0xfe:
-                       case 0xfc:
-                       case 0xf8:
-                       case 0xf0:
-                       case 0xe0:
-                       case 0xc0:
-                       case 0x80:
-                               final = 1;
-                               break;
-                       default:
-                               final = 1;
-                               illegal = 1;
-                               break;
-                       }
-                       if (!illegal)
-                               rt->mask.s6_addr[i++] &= *p;
-                       else
-                               rt->mask.s6_addr[i++] = 0x00;
-               }
-               while (i < sizeof(rt->mask.s6_addr))
-                       rt->mask.s6_addr[i++] = 0x00;
-       } else
-               ipv6_mask(&rt->mask, 128);
-       COPYOUT6(rt->gate, rti_info[RTAX_GATEWAY]);
-       DESCOPE(&rt->gate);
-       rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
-
-       if (rtm->rtm_index)
-               rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index);
-       else if (rtm->rtm_addrs & RTA_IFP)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_IFP]);
-       else if (rtm->rtm_addrs & RTA_GATEWAY)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
-
-       /* If we don't have an interface and it's a host route, it maybe
-        * to a local ip via the loopback interface. */
-       if (rt->iface == NULL &&
-           !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
-       {
-               struct ipv6_addr *ia;
-
-               if ((ia = ipv6_findaddr(ctx, &rt->dest, 0)))
-                       rt->iface = ia->iface;
-       }
-
-       if (rt->iface == NULL) {
-               errno = ESRCH;
-               return -1;
-       }
-       return 0;
-}
-
-int
-if_route6(unsigned char cmd, const struct rt6 *rt)
-{
-       struct sockaddr_in6 dst = {
-               .sin6_family = AF_INET6,
-               .sin6_len = sizeof(dst),
-               .sin6_addr = rt->dest
-       };
-       struct sockaddr_in6 mask = {
-               .sin6_family = AF_INET6,
-               .sin6_len = sizeof(mask),
-               .sin6_addr = rt->mask
-       };
-       struct sockaddr_in6 gate = {
-               .sin6_family = AF_INET6,
-               .sin6_len = sizeof(gate),
-               .sin6_addr = rt->gate
-       };
-       struct sockaddr_in6 src = {
-               .sin6_family = AF_INET6,
-               .sin6_len = sizeof(src),
-               .sin6_addr = rt->src
-       };
-       struct sockaddr_in6 *g;
-       int addrs, flags;
-#ifdef RTP_CONNECTED
-       int priority = 0;
-#endif
-
-       addrs = RTA_NETMASK;
-       flags = (int)rt->flags;
-#ifdef RTF_PINNED
-       if (cmd != RTM_ADD)
-               flags |= RTF_PINNED;
-#endif
-
-       if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
-#ifdef RTF_CLONING
-               flags |= RTF_CLONING;
-#endif
-#ifdef RTF_CONNECTED
-               flags |= RTF_CONNECTED;
-#endif
-#ifdef RTP_CONNECTED
-               priority = RTP_CONNECTED;
-#endif
-       } else
-               flags |= RTF_GATEWAY | RTF_STATIC;
-
-       if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate))
-               g = NULL;
-       else {
-               g = &gate;
-               ifa_scope(g, rt->iface->index);
-       }
-
-       return if_rtmsg(cmd, rt->iface, addrs, flags,
-#ifdef RTP_CONNECTED
-           priority,
-#endif
-           (struct sockaddr *)&dst, (struct sockaddr *)&mask,
-           (struct sockaddr *)g, (struct sockaddr *)&src, rt->mtu);
-}
-
-int
-if_initrt6(struct dhcpcd_ctx *ctx)
-{
-       struct rt_msghdr *rtm;
-       int mib[6];
-       size_t needed;
-       char *buf, *p, *end;
-       struct rt6 rt;
-
-       ipv6_freerts(&ctx->ipv6->kroutes);
-
-       mib[0] = CTL_NET;
-       mib[1] = PF_ROUTE;
-       mib[2] = 0;
-       mib[3] = AF_INET6;
-       mib[4] = NET_RT_DUMP;
-       mib[5] = 0;
-
-       if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
-               return -1;
-       if (needed == 0)
-               return 0;
-       if ((buf = malloc(needed)) == NULL)
-               return -1;
-       if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) {
-               free(buf);
-               return -1;
-       }
-
-       end = buf + needed;
-       for (p = buf; p < end; p += rtm->rtm_msglen) {
-               rtm = (void *)p;
-               if (if_copyrt6(ctx, &rt, rtm) == 0)
-                       ipv6_handlert(ctx, RTM_ADD, &rt);
-       }
-       free(buf);
-       return 0;
-}
-
 #if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS))
 int
 if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
@@ -1351,60 +1089,36 @@ if_ownmsgpid(struct dhcpcd_ctx *ctx, pid_t pid, int seq)
 static void
 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
-       const struct sockaddr *sa;
+       struct rt rt;
 
        if (if_ownmsgpid(ctx, rtm->rtm_pid, rtm->rtm_seq))
                return;
 
-       sa = (const void *)(rtm + 1);
-       switch (sa->sa_family) {
-#ifdef INET
-       case AF_INET:
-       {
-               struct rt rt;
+       if (if_copyrt(ctx, &rt, rtm) == -1)
+               return;
 
-               if (if_copyrt(ctx, &rt, rtm) == 0)
-                       ipv4_handlert(ctx, rtm->rtm_type, &rt, 0);
-               break;
-       }
-#endif
 #ifdef INET6
-       case AF_INET6:
-       {
-               struct rt6 rt6;
-
-               if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
-                       break;
-               /*
-                * BSD announces host routes.
-                * As such, we should be notified of reachability by its
-                * existance with a hardware address.
-                */
-               if (rtm->rtm_flags & (RTF_HOST)) {
-                       const struct sockaddr *rti_info[RTAX_MAX];
-                       struct in6_addr dst6;
-                       struct sockaddr_dl sdl;
-
-                       get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
-                       COPYOUT6(dst6, rti_info[RTAX_DST]);
-                       DESCOPE(&dst6);
-                       if (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK)
-                               memcpy(&sdl, rti_info[RTAX_GATEWAY],
-                                   sizeof(sdl));
-                       else
-                               sdl.sdl_alen = 0;
-                       ipv6nd_neighbour(ctx, &dst6,
-                           rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ?
-                           IPV6ND_REACHABLE : 0);
-                       break;
-               }
+       /*
+        * BSD announces host routes.
+        * As such, we should be notified of reachability by its
+        * existance with a hardware address.
+        */
+       if (rt.rt_dest.sa_family == AF_INET6 && rt.rt_flags & RTF_HOST) {
+               struct sockaddr_in6 dest;
+               struct sockaddr_dl sdl;
 
-               if (if_copyrt6(ctx, &rt6, rtm) == 0)
-                       ipv6_handlert(ctx, rtm->rtm_type, &rt6);
-               break;
+               memcpy(&dest, &rt.rt_dest, rt.rt_dest.sa_len);
+               if (rt.rt_gateway.sa_family == AF_LINK)
+                       memcpy(&sdl, &rt.rt_gateway, rt.rt_gateway.sa_len);
+               else
+                       sdl.sdl_alen = 0;
+               ipv6nd_neighbour(ctx, &dest.sin6_addr,
+                   rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ?
+                   IPV6ND_REACHABLE : 0);
        }
 #endif
-       }
+
+       rt_recvrt(rtm->rtm_type, &rt);
 }
 
 static void
@@ -1509,10 +1223,8 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
 
                sin6 = (const void *)rti_info[RTAX_IFA];
                addr6 = sin6->sin6_addr;
-               DESCOPE(&addr6);
                sin6 = (const void *)rti_info[RTAX_NETMASK];
                mask6 = sin6->sin6_addr;
-               DESCOPE(&mask6);
 
 #ifndef HAVE_IFAM_ADDRFLAGS
                if (ifam->ifam_type == RTM_DELADDR ||
index 4836d72d6f7a3b8982e86199546b9978b525de85..54c740e697e179ac938797db6553c239e7164245 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * dhcpcd - DHCP client daemon
+ * Linux interface driver for dhcpcd
  * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
  * All rights reserved
 
@@ -66,6 +66,8 @@
 #include "ipv4ll.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
+#include "route.h"
+#include "sa.h"
 
 #ifdef HAVE_NL80211_H
 #include <linux/genetlink.h>
@@ -386,7 +388,6 @@ recv_again:
        return r;
 }
 
-#ifdef INET
 static int
 if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
 {
@@ -394,6 +395,7 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
        struct rtmsg *rtm;
        struct rtattr *rta;
        unsigned int ifindex;
+       struct sockaddr *sa;
 
        len = nlm->nlmsg_len - sizeof(*nlm);
        if (len < sizeof(*rtm)) {
@@ -401,37 +403,35 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
                return -1;
        }
        rtm = (struct rtmsg *)NLMSG_DATA(nlm);
-       if (rtm->rtm_table != RT_TABLE_MAIN || rtm->rtm_family != AF_INET)
+       if (rtm->rtm_table != RT_TABLE_MAIN)
                return -1;
 
        memset(rt, 0, sizeof(*rt));
        if (rtm->rtm_type == RTN_UNREACHABLE)
-               rt->flags = RTF_REJECT;
+               rt->rt_flags |= RTF_REJECT;
        if (rtm->rtm_scope == RT_SCOPE_HOST)
-               rt->flags |= RTF_HOST;
+               rt->rt_flags |= RTF_HOST;
 
        rta = (struct rtattr *)RTM_RTA(rtm);
        len = RTM_PAYLOAD(nlm);
        while (RTA_OK(rta, len)) {
+               sa = NULL;
                switch (rta->rta_type) {
                case RTA_DST:
-                       memcpy(&rt->dest.s_addr, RTA_DATA(rta),
-                           sizeof(rt->dest.s_addr));
+                       sa = &rt->rt_dest;
                        break;
                case RTA_GATEWAY:
-                       memcpy(&rt->gate.s_addr, RTA_DATA(rta),
-                           sizeof(rt->gate.s_addr));
+                       sa = &rt->rt_gateway;
                        break;
                case RTA_PREFSRC:
-                       memcpy(&rt->src.s_addr, RTA_DATA(rta),
-                           sizeof(rt->src.s_addr));
+                       sa = &rt->rt_ifa;
                        break;
                case RTA_OIF:
                        ifindex = *(unsigned int *)RTA_DATA(rta);
-                       rt->iface = if_findindex(ctx->ifaces, ifindex);
+                       rt->rt_ifp = if_findindex(ctx->ifaces, ifindex);
                        break;
                case RTA_PRIORITY:
-                       rt->metric = *(unsigned int *)RTA_DATA(rta);
+                       rt->rt_metric = *(unsigned int *)RTA_DATA(rta);
                        break;
                case RTA_METRICS:
                {
@@ -443,7 +443,7 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
                        while (RTA_OK(r2, l2)) {
                                switch (r2->rta_type) {
                                case RTAX_MTU:
-                                       rt->mtu = *(unsigned int *)RTA_DATA(r2);
+                                       rt->rt_mtu = *(unsigned int *)RTA_DATA(r2);
                                        break;
                                }
                                r2 = RTA_NEXT(r2, l2);
@@ -452,11 +452,27 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
                }
                }
 
+               if (sa != NULL) {
+                       socklen_t salen;
+
+                       sa->sa_family = rtm->rtm_family;
+                       salen = sa_addrlen(sa);
+                       memcpy((char *)sa + sa_addroffset(sa), RTA_DATA(rta),
+                           MIN(salen, RTA_PAYLOAD(rta)));
+               }
+
                rta = RTA_NEXT(rta, len);
        }
 
-       inet_cidrtoaddr(rtm->rtm_dst_len, &rt->mask);
-       if (rt->iface == NULL && rt->src.s_addr != INADDR_ANY) {
+       /* If no RTA_DST set the unspecified address for the family. */
+       if (rt->rt_dest.sa_family == AF_UNSPEC)
+               rt->rt_dest.sa_family = rtm->rtm_family;
+
+       rt->rt_netmask.sa_family = rtm->rtm_family;
+       sa_fromprefix(&rt->rt_netmask, rtm->rtm_dst_len);
+
+       #if 0
+       if (rt->rtp_ifp == NULL && rt->src.s_addr != INADDR_ANY) {
                struct ipv4_addr *ap;
 
                /* For some reason the default route comes back with the
@@ -465,102 +481,24 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)
                if ((ap = ipv4_findaddr(ctx, &rt->src)))
                        rt->iface = ap->iface;
        }
+       #endif
 
-       if (rt->iface == NULL) {
-               errno = ESRCH;
-               return -1;
-       }
-       return 0;
-}
-#endif
-
-#ifdef INET6
-static int
-if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, struct nlmsghdr *nlm)
-{
-       size_t len;
-       struct rtmsg *rtm;
-       struct rtattr *rta;
-       unsigned int ifindex;
-
-       len = nlm->nlmsg_len - sizeof(*nlm);
-       if (len < sizeof(*rtm)) {
-               errno = EBADMSG;
-               return -1;
-       }
-       rtm = (struct rtmsg *)NLMSG_DATA(nlm);
-       if (rtm->rtm_table != RT_TABLE_MAIN || rtm->rtm_family != AF_INET6)
-               return -1;
-
-       memset(rt, 0, sizeof(*rt));
-       if (rtm->rtm_type == RTN_UNREACHABLE)
-               rt->flags = RTF_REJECT;
-       if (rtm->rtm_scope == RT_SCOPE_HOST)
-               rt->flags |= RTF_HOST;
-       ipv6_mask(&rt->mask, rtm->rtm_dst_len);
-
-       rta = (struct rtattr *)RTM_RTA(rtm);
-       len = RTM_PAYLOAD(nlm);
-       while (RTA_OK(rta, len)) {
-               switch (rta->rta_type) {
-               case RTA_DST:
-                       memcpy(&rt->dest.s6_addr, RTA_DATA(rta),
-                           sizeof(rt->dest.s6_addr));
-                       break;
-               case RTA_GATEWAY:
-                       memcpy(&rt->gate.s6_addr, RTA_DATA(rta),
-                           sizeof(rt->gate.s6_addr));
-                       break;
-               case RTA_OIF:
-                       ifindex = *(unsigned int *)RTA_DATA(rta);
-                       rt->iface = if_findindex(ctx->ifaces, ifindex);
-                       break;
-               case RTA_PRIORITY:
-                       rt->metric = *(unsigned int *)RTA_DATA(rta);
-                       break;
-               case RTA_METRICS:
-               {
-                       struct rtattr *r2;
-                       size_t l2;
-
-                       l2 = rta->rta_len;
-                       r2 = (struct rtattr *)RTA_DATA(rta);
-                       while (RTA_OK(r2, l2)) {
-                               switch (r2->rta_type) {
-                               case RTAX_MTU:
-                                       rt->mtu = *(unsigned int *)RTA_DATA(r2);
-                                       break;
-                               }
-                               r2 = RTA_NEXT(r2, l2);
-                       }
-                       break;
-               }
-               }
-               rta = RTA_NEXT(rta, len);
-       }
-
-       if (rt->iface == NULL) {
+       if (rt->rt_ifp == NULL) {
                errno = ESRCH;
                return -1;
        }
        return 0;
 }
-#endif
 
 static int
 link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
     struct nlmsghdr *nlm)
 {
        size_t len;
-       struct rtmsg *rtm;
        int cmd;
        struct priv *priv;
-#ifdef INET
        struct rt rt;
-#endif
-#ifdef INET6
-       struct rt6 rt6;
-#endif
+
        switch (nlm->nlmsg_type) {
        case RTM_NEWROUTE:
                cmd = RTM_ADD;
@@ -573,7 +511,7 @@ link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
        }
 
        len = nlm->nlmsg_len - sizeof(*nlm);
-       if (len < sizeof(*rtm)) {
+       if (len < sizeof(struct rtmsg)) {
                errno = EBADMSG;
                return -1;
        }
@@ -583,21 +521,8 @@ link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
        if (nlm->nlmsg_pid == priv->route_pid)
                return 0;
 
-       rtm = NLMSG_DATA(nlm);
-       switch (rtm->rtm_family) {
-#ifdef INET
-       case AF_INET:
-               if (if_copyrt(ctx, &rt, nlm) == 0)
-                       ipv4_handlert(ctx, cmd, &rt, 0);
-               break;
-#endif
-#ifdef INET6
-       case AF_INET6:
-               if (if_copyrt6(ctx, &rt6, nlm) == 0)
-                       ipv6_handlert(ctx, cmd, &rt6);
-               break;
-#endif
-       }
+       if (if_copyrt(ctx, &rt, nlm) == 0)
+               rt_recvrt(cmd, &rt);
 
        return 0;
 }
@@ -1198,6 +1123,123 @@ struct nlmr
        char buffer[256];
 };
 
+int
+if_route(unsigned char cmd, const struct rt *rt)
+{
+       struct nlmr nlm;
+       bool gateway_unspec;
+
+       printf ("\nROUTE %s\n\n", cmd == RTM_DELETE ? "DELETE" : "ADD");
+
+       memset(&nlm, 0, sizeof(nlm));
+       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       switch (cmd) {
+       case RTM_CHANGE:
+               nlm.hdr.nlmsg_type = RTM_NEWROUTE;
+               nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE;
+               break;
+       case RTM_ADD:
+               nlm.hdr.nlmsg_type = RTM_NEWROUTE;
+               nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
+               break;
+       case RTM_DELETE:
+               nlm.hdr.nlmsg_type = RTM_DELROUTE;
+               break;
+       }
+       nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
+       nlm.rt.rtm_family = (unsigned char)rt->rt_dest.sa_family;
+       nlm.rt.rtm_table = RT_TABLE_MAIN;
+
+       gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
+
+       if (cmd == RTM_DELETE) {
+               nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
+       } else {
+               /* Address generated routes are RTPROT_KERNEL,
+                * otherwise RTPROT_BOOT */
+               if (rt->rt_dflags & RTDF_IFA_ROUTE)
+                       nlm.rt.rtm_protocol = RTPROT_KERNEL;
+               else
+                       nlm.rt.rtm_protocol = RTPROT_BOOT;
+               if (rt->rt_ifp->flags & IFF_LOOPBACK)
+                       nlm.rt.rtm_scope = RT_SCOPE_HOST;
+               else if (gateway_unspec || sa_is_allones(&rt->rt_netmask))
+                       nlm.rt.rtm_scope = RT_SCOPE_LINK;
+               else
+                       nlm.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+               nlm.rt.rtm_type = RTN_UNICAST;
+       }
+
+#define ADDSA(type, sa)                                                        \
+       add_attr_l(&nlm.hdr, sizeof(nlm), (type),                       \
+           (const char *)(sa) + sa_addroffset((sa)),                   \
+           (unsigned short)sa_addrlen((sa)));
+       nlm.rt.rtm_dst_len = (unsigned char)sa_toprefix(&rt->rt_netmask);
+       ADDSA(RTA_DST, &rt->rt_dest);
+       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
+               if (!gateway_unspec)
+                       ADDSA(RTA_GATEWAY, &rt->rt_gateway);
+               /* Cannot add tentative source addresses.
+                * We don't know this here, so just skip INET6 ifa's.*/
+               if (!sa_is_unspecified(&rt->rt_ifa) &&
+                   rt->rt_ifa.sa_family != AF_INET6)
+                       ADDSA(RTA_PREFSRC, &rt->rt_ifa);
+               if (rt->rt_mtu) {
+                       char metricsbuf[32];
+                       struct rtattr *metrics = (void *)metricsbuf;
+
+                       metrics->rta_type = RTA_METRICS;
+                       metrics->rta_len = RTA_LENGTH(0);
+                       rta_add_attr_32(metrics, sizeof(metricsbuf),
+                           RTAX_MTU, rt->rt_mtu);
+                       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,
+                           RTA_DATA(metrics),
+                           (unsigned short)RTA_PAYLOAD(metrics));
+               }
+       }
+
+       if (!sa_is_loopback(&rt->rt_gateway))
+               add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->rt_ifp->index);
+
+       if (rt->rt_metric != 0)
+               add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY,
+                   rt->rt_metric);
+
+       return send_netlink(rt->rt_ifp->ctx, NULL,
+           NETLINK_ROUTE, &nlm.hdr, NULL);
+}
+
+static int
+_if_initrt(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+    struct nlmsghdr *nlm)
+{
+       struct rt rt;
+
+       if (if_copyrt(ctx, &rt, nlm) == 0) {
+               rt.rt_dflags |= RTDF_INIT;
+               rt_recvrt(RTM_ADD, &rt);
+       }
+       return 0;
+}
+
+int
+if_initrt(struct dhcpcd_ctx *ctx)
+{
+       struct nlmr nlm;
+
+       rt_headclear(&ctx->kroutes);
+
+       memset(&nlm, 0, sizeof(nlm));
+       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       nlm.hdr.nlmsg_type = RTM_GETROUTE;
+       nlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
+       nlm.rt.rtm_table = RT_TABLE_MAIN;
+       nlm.rt.rtm_family = AF_UNSPEC;
+
+       return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, &_if_initrt);
+}
+
+
 #ifdef INET
 const char *if_pfname = "Packet Socket";
 
@@ -1369,112 +1411,6 @@ if_address(unsigned char cmd, const struct ipv4_addr *addr)
        return retval;
 }
 
-int
-if_route(unsigned char cmd, const struct rt *rt)
-{
-       struct nlmr nlm;
-
-       memset(&nlm, 0, sizeof(nlm));
-       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-       switch (cmd) {
-       case RTM_CHANGE:
-               nlm.hdr.nlmsg_type = RTM_NEWROUTE;
-               nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE;
-               break;
-       case RTM_ADD:
-               nlm.hdr.nlmsg_type = RTM_NEWROUTE;
-               nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
-               break;
-       case RTM_DELETE:
-               nlm.hdr.nlmsg_type = RTM_DELROUTE;
-               break;
-       }
-       nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
-       nlm.rt.rtm_family = AF_INET;
-       nlm.rt.rtm_table = RT_TABLE_MAIN;
-
-       if (cmd == RTM_DELETE) {
-               nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
-       } else {
-               /* Subnet routes are RTPROT_KERNEL otherwise RTPROT_BOOT */
-               if (rt->gate.s_addr == ntohl(INADDR_ANY))
-                       nlm.rt.rtm_protocol = RTPROT_KERNEL;
-               else
-                       nlm.rt.rtm_protocol = RTPROT_BOOT;
-               if (rt->iface->flags & IFF_LOOPBACK)
-                       nlm.rt.rtm_scope = RT_SCOPE_HOST;
-               else if (rt->gate.s_addr == INADDR_ANY ||
-                   (rt->gate.s_addr == rt->dest.s_addr &&
-                       rt->mask.s_addr == INADDR_BROADCAST))
-                       nlm.rt.rtm_scope = RT_SCOPE_LINK;
-               else
-                       nlm.rt.rtm_scope = RT_SCOPE_UNIVERSE;
-               nlm.rt.rtm_type = RTN_UNICAST;
-       }
-
-       nlm.rt.rtm_dst_len = inet_ntocidr(rt->mask);
-       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_DST,
-           &rt->dest.s_addr, sizeof(rt->dest.s_addr));
-       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
-               if (rt->gate.s_addr != htonl(INADDR_ANY))
-                       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
-                           &rt->gate.s_addr, sizeof(rt->gate.s_addr));
-               if (rt->src.s_addr != ntohl(INADDR_ANY)) {
-                       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_PREFSRC,
-                           &rt->src.s_addr, sizeof(rt->src.s_addr));
-               }
-               if (rt->mtu) {
-                       char metricsbuf[32];
-                       struct rtattr *metrics = (void *)metricsbuf;
-
-                       metrics->rta_type = RTA_METRICS;
-                       metrics->rta_len = RTA_LENGTH(0);
-                       rta_add_attr_32(metrics, sizeof(metricsbuf),
-                           RTAX_MTU, rt->mtu);
-                       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,
-                           RTA_DATA(metrics),
-                           (unsigned short)RTA_PAYLOAD(metrics));
-               }
-       }
-
-       if (rt->gate.s_addr != htonl(INADDR_LOOPBACK))
-               add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->iface->index);
-
-       if (rt->metric)
-               add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric);
-
-       return send_netlink(rt->iface->ctx, NULL,
-           NETLINK_ROUTE, &nlm.hdr, NULL);
-}
-
-static int
-_if_initrt(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
-    struct nlmsghdr *nlm)
-{
-       struct rt rt;
-
-       if (if_copyrt(ctx, &rt, nlm) == 0)
-               ipv4_handlert(ctx, RTM_ADD, &rt, 1);
-       return 0;
-}
-
-int
-if_initrt(struct dhcpcd_ctx *ctx)
-{
-       struct nlmr nlm;
-
-       ipv4_freerts(ctx->ipv4_kroutes);
-
-       memset(&nlm, 0, sizeof(nlm));
-       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-       nlm.hdr.nlmsg_type = RTM_GETROUTE;
-       nlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
-       nlm.rt.rtm_family = AF_INET;
-       nlm.rt.rtm_table = RT_TABLE_MAIN;
-
-       return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, &_if_initrt);
-}
-
 int
 if_addrflags(__unused const struct interface *ifp,
 __unused const struct in_addr *addr, __unused const char *alias)
@@ -1550,114 +1486,6 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia)
            NETLINK_ROUTE, &nlm.hdr, NULL);
 }
 
-int
-if_route6(unsigned char cmd, const struct rt6 *rt)
-{
-       struct nlmr nlm;
-
-       memset(&nlm, 0, sizeof(nlm));
-       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-       switch (cmd) {
-       case RTM_CHANGE:
-               nlm.hdr.nlmsg_type = RTM_NEWROUTE;
-               nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE;
-               break;
-       case RTM_ADD:
-               nlm.hdr.nlmsg_type = RTM_NEWROUTE;
-               nlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;
-               break;
-       case RTM_DELETE:
-               nlm.hdr.nlmsg_type = RTM_DELROUTE;
-               break;
-       }
-       nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
-       nlm.rt.rtm_family = AF_INET6;
-       nlm.rt.rtm_table = RT_TABLE_MAIN;
-
-       if (cmd == RTM_DELETE)
-               nlm.rt.rtm_scope = RT_SCOPE_NOWHERE;
-       else {
-               /* None interface subnet routes are static. */
-               if (rt->iface->flags & IFF_LOOPBACK)
-                       nlm.rt.rtm_scope = RT_SCOPE_HOST;
-               else if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
-                       nlm.rt.rtm_protocol = RTPROT_KERNEL;
-                       nlm.rt.rtm_scope = RT_SCOPE_LINK;
-               } else
-                       nlm.rt.rtm_protocol = RTPROT_BOOT;
-               if (rt->flags & RTF_REJECT)
-                       nlm.rt.rtm_type = RTN_UNREACHABLE;
-               else
-                       nlm.rt.rtm_type = RTN_UNICAST;
-       }
-
-       nlm.rt.rtm_dst_len = ipv6_prefixlen(&rt->mask);
-       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_DST,
-           &rt->dest.s6_addr, sizeof(rt->dest.s6_addr));
-
-       if (cmd == RTM_ADD && !IN6_IS_ADDR_UNSPECIFIED(&rt->gate))
-               add_attr_l(&nlm.hdr, sizeof(nlm), RTA_GATEWAY,
-                   &rt->gate.s6_addr, sizeof(rt->gate.s6_addr));
-
-       add_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->iface->index);
-       if (rt->metric)
-               add_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->metric);
-
-       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
-#if 0
-               /* XXX This fails to work, why? */
-               if (!IN6_IS_ADDR_UNSPECIFIED(&rt->src)) {
-                       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_PREFSRC,
-                           &rt->src.s6_addr, sizeof(rt->src.s6_addr));
-               }
-#endif
-               if (rt->mtu) {
-                       char metricsbuf[32];
-                       struct rtattr *metrics = (void *)metricsbuf;
-
-                       metrics->rta_type = RTA_METRICS;
-                       metrics->rta_len = RTA_LENGTH(0);
-                       rta_add_attr_32(metrics, sizeof(metricsbuf),
-                           RTAX_MTU, rt->mtu);
-                       add_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,
-                           RTA_DATA(metrics),
-                           (unsigned short)RTA_PAYLOAD(metrics));
-               }
-       }
-
-       return send_netlink(rt->iface->ctx, NULL,
-           NETLINK_ROUTE, &nlm.hdr, NULL);
-}
-
-static int
-_if_initrt6(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
-    struct nlmsghdr *nlm)
-{
-       struct rt6 rt;
-
-       if (if_copyrt6(ctx, &rt, nlm) == 0)
-               ipv6_handlert(ctx, RTM_ADD, &rt);
-       return 0;
-}
-
-int
-if_initrt6(struct dhcpcd_ctx *ctx)
-{
-       struct nlmr nlm;
-
-       ipv6_freerts(&ctx->ipv6->kroutes);
-
-       memset(&nlm, 0, sizeof(nlm));
-       nlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-       nlm.hdr.nlmsg_type = RTM_GETROUTE;
-       nlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
-       nlm.hdr.nlmsg_flags |= NLM_F_REQUEST;
-       nlm.rt.rtm_family = AF_INET6;
-       nlm.rt.rtm_table = RT_TABLE_MAIN;
-
-       return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, &_if_initrt6);
-}
-
 int
 if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
     __unused const char *alias)
index f08a7a8749f6c32a953915f3098f795f726ec114..c282adde756a085dadabab8ea990aa5361e00108 100644 (file)
@@ -52,6 +52,7 @@
 #include "if.h"
 #include "if-options.h"
 #include "ipv4.h"
+#include "sa.h"
 
 /* These options only make sense in the config file, so don't use any
    valid short options for them */
@@ -1071,6 +1072,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
                    strncmp(arg, "ms_classless_static_routes=",
                        strlen("ms_classless_static_routes=")) == 0)
                {
+                       struct in_addr addr3;
+
                        fp = np = strwhite(p);
                        if (np == NULL) {
                                logger(ctx, LOG_ERR,
@@ -1079,52 +1082,31 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
                        }
                        *np++ = '\0';
                        np = strskipwhite(np);
-                       if (ifo->routes == NULL) {
-                               ifo->routes = malloc(sizeof(*ifo->routes));
-                               if (ifo->routes == NULL) {
-                                       logger(ctx, LOG_ERR,
-                                           "%s: %m", __func__);
-                                       return -1;
-                               }
-                               TAILQ_INIT(ifo->routes);
-                       }
-                       rt = calloc(1, sizeof(*rt));
-                       if (rt == NULL) {
-                               logger(ctx, LOG_ERR, "%s: %m", __func__);
+                       if (parse_addr(ctx, &addr, &addr2, p) == -1 ||
+                           parse_addr(ctx, &addr3, NULL, np) == -1)
+                       {
                                *fp = ' ';
                                return -1;
                        }
-                       if (parse_addr(ctx, &rt->dest, &rt->mask, p) == -1 ||
-                           parse_addr(ctx, &rt->gate, NULL, np) == -1)
-                       {
-                               free(rt);
+                       if ((rt = rt_new(if_find(ctx->ifaces, ifname))) == NULL) {
                                *fp = ' ';
                                return -1;
                        }
-                       TAILQ_INSERT_TAIL(ifo->routes, rt, next);
+                       sa_in_init(&rt->rt_dest, &addr);
+                       sa_in_init(&rt->rt_netmask, &addr2);
+                       sa_in_init(&rt->rt_gateway, &addr3);
+                       TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
                        *fp = ' ';
                } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
-                       if (ifo->routes == NULL) {
-                               ifo->routes = malloc(sizeof(*ifo->routes));
-                               if (ifo->routes == NULL) {
-                                       logger(ctx, LOG_ERR,
-                                           "%s: %m", __func__);
-                                       return -1;
-                               }
-                               TAILQ_INIT(ifo->routes);
-                       }
-                       rt = calloc(1, sizeof(*rt));
-                       if (rt == NULL) {
-                               logger(ctx, LOG_ERR, "%s: %m", __func__);
+                       if (parse_addr(ctx, &addr, NULL, p) == -1)
                                return -1;
-                       }
-                       rt->dest.s_addr = INADDR_ANY;
-                       rt->mask.s_addr = INADDR_ANY;
-                       if (parse_addr(ctx, &rt->gate, NULL, p) == -1) {
-                               free(rt);
+                       if ((rt = rt_new(if_find(ctx->ifaces, ifname))) == NULL)
                                return -1;
-                       }
-                       TAILQ_INSERT_TAIL(ifo->routes, rt, next);
+                       addr2.s_addr = INADDR_ANY;
+                       sa_in_init(&rt->rt_dest, &addr2);
+                       sa_in_init(&rt->rt_netmask, &addr2);
+                       sa_in_init(&rt->rt_gateway, &addr);
+                       TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next);
                } else if (strncmp(arg, "interface_mtu=",
                    strlen("interface_mtu=")) == 0 ||
                    strncmp(arg, "mtu=", strlen("mtu=")) == 0)
@@ -2260,6 +2242,7 @@ default_config(struct dhcpcd_ctx *ctx)
        ifo->reboot = DEFAULT_REBOOT;
        ifo->metric = -1;
        ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
+       TAILQ_INIT(&ifo->routes);
 #ifdef AUTH
        TAILQ_INIT(&ifo->auth.tokens);
 #endif
@@ -2599,7 +2582,7 @@ free_options(struct if_options *ifo)
                                free(ifo->config[i++]);
                        free(ifo->config);
                }
-               ipv4_freeroutes(ifo->routes);
+               rt_headclear(&ifo->routes);
                free(ifo->script);
                free(ifo->arping);
                free(ifo->blacklist);
index 991672a01d44bcb1a2056dd48c907d93aa96cca4..bbd26eab9adeab628578212849441dbf3c10476b 100644 (file)
@@ -38,6 +38,7 @@
 #include <stdint.h>
 
 #include "auth.h"
+#include "route.h"
 
 /* Don't set any optional arguments here so we retain POSIX
  * compatibility with getopt */
@@ -176,7 +177,7 @@ struct if_options {
 
        struct in_addr req_addr;
        struct in_addr req_mask;
-       struct rt_head *routes;
+       struct rt_head routes;
        struct in6_addr req_addr6;
        uint8_t req_prefix_len;
        unsigned int mtu;
index 602f824e88aa65472425dbb8cdb2e4e1d2b6dc9c..3aeeb20b82e31a849809fa3a11abf339e708b8c4 100644 (file)
--- a/if-sun.c
+++ b/if-sun.c
@@ -61,6 +61,8 @@
 #include "ipv4.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
+#include "route.h"
+#include "sa.h"
 
 #ifndef ARP_MOD_NAME
 #  define ARP_MOD_NAME        "arp"
@@ -84,9 +86,7 @@
                    (sa))->sin6_addr;                                         \
        } while (0)
 
-#ifndef CLLADDR
-#  define CLLADDR(s) (const void *)((s)->sdl_data + (s)->sdl_nlen)
-#endif
+#define COPYSA(dst, src) memcpy((dst), (src), salen((src)))
 
 #ifdef INET
 /* Instead of using DLPI directly, we use libdlpi which is
@@ -446,7 +446,6 @@ if_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)
        return NULL;
 }
 
-#ifdef INET
 static void
 if_finishrt(struct rt *rt)
 {
@@ -455,141 +454,79 @@ if_finishrt(struct rt *rt)
         * of the owning address.
         * dhcpcd has a blank gateway here to indicate a
         * subnet route. */
-       if (rt->gate.s_addr != ntohl(INADDR_ANY) &&
-           ipv4_iffindaddr(UNCONST(rt->iface), &rt->gate, NULL))
-               rt->gate.s_addr = ntohl(INADDR_ANY);
-
-       /* Solaris likes to set route MTU to match
-        * interface MTU when adding routes.
-        * This confuses dhcpcd as it expects MTU to be 0
-        * when no explicit MTU has been set. */
-       if (rt->mtu == (unsigned int)if_getmtu(rt->iface))
-               rt->mtu = 0;
-}
-
-static int
-if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
-{
-       const struct sockaddr *sa, *rti_info[RTAX_MAX];
-
-       sa = (const void *)(rtm + 1);
-       if (sa->sa_family != AF_INET)
-               return -1;
-       if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
-               return -1;
-
-       get_addrs(rtm->rtm_addrs, sa, rti_info);
-       memset(rt, 0, sizeof(*rt));
-       rt->flags = (unsigned int)rtm->rtm_flags;
-       COPYOUT(rt->dest, rti_info[RTAX_DST]);
-       if (rtm->rtm_addrs & RTA_NETMASK)
-               COPYOUT(rt->mask, rti_info[RTAX_NETMASK]);
-       else
-               rt->mask.s_addr = INADDR_BROADCAST;
-       COPYOUT(rt->gate, rti_info[RTAX_GATEWAY]);
-       COPYOUT(rt->src, rti_info[RTAX_SRC]);
-       rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
-
-       if (rtm->rtm_index)
-               rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index);
-       else if (rtm->rtm_addrs & RTA_IFP)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_IFP]);
-       else if (rtm->rtm_addrs & RTA_GATEWAY)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
-
-       /* If we don't have an interface and it's a host route, it maybe
-        * to a local ip via the loopback interface. */
-       if (rt->iface == NULL &&
-           !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
-       {
-               struct ipv4_addr *ia;
-
-               if ((ia = ipv4_findaddr(ctx, &rt->dest)))
-                       rt->iface = ia->iface;
-       }
-
-       if (rt->iface == NULL) {
-               errno = ESRCH;
-               return -1;
-       }
-
-       if_finishrt(rt);
+       if (!sa_is_unspecified(&rt->rt_gateway)) {
+               switch(rt->rt_gateway.sa_family) {
+#ifdef INET
+               case AF_INET:
+               {
+                       struct in_addr *in;
 
-       return 0;
-}
+                       in = &satosin(&rt->rt_gateway)->sin_addr;
+                       if (ipv4_iffindaddr(rt->rt_ifp, in, NULL))
+                               in->s_addr = INADDR_ANY;
+                       break;
+               }
 #endif
-
 #ifdef INET6
-static void
-if_finishrt6(struct rt6 *rt)
-{
+               case AF_INET6:
+               {
+                       struct in6_addr *in6;
 
-       /* Solaris has a subnet route with the gateway
-        * of the owning address.
-        * dhcpcd has a blank gateway here to indicate a
-        * subnet route. */
-       if (!IN6_IS_ADDR_UNSPECIFIED(&rt->gate) &&
-           ipv6_iffindaddr(UNCONST(rt->iface), &rt->gate, 0))
-               rt->gate = in6addr_any;
+                       in6 = &satosin6(&rt->rt_gateway)->sin6_addr;
+                       if (ipv6_iffindaddr(rt->rt_ifp, in6, 0))
+                               *in6 = in6addr_any;
+                       break;
+               }
+#endif
+               }
+       }
 
-       /* Solarais likes to set route MTU to match
+       /* Solaris likes to set route MTU to match
         * interface MTU when adding routes.
         * This confuses dhcpcd as it expects MTU to be 0
         * when no explicit MTU has been set. */
-       if (rt->mtu == (unsigned int)if_getmtu(rt->iface))
-               rt->mtu = 0;
+       if (rt->rt_mtu == (unsigned int)if_getmtu(rt->rt_ifp))
+               rt->rt_mtu = 0;
 }
 
 static int
-if_copyrt6(struct dhcpcd_ctx *ctx, struct rt6 *rt, const struct rt_msghdr *rtm)
+if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
 {
-       const struct sockaddr *sa, *rti_info[RTAX_MAX];
+       const struct sockaddr *rti_info[RTAX_MAX];
 
-       sa = (const void *)(rtm + 1);
-       if (sa->sa_family != AF_INET6)
-               return -1;
        if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
                return -1;
 
-       get_addrs(rtm->rtm_addrs, sa, rti_info);
+       get_addrs(rtm->rtm_addrs, rtm + 1, rti_info);
        memset(rt, 0, sizeof(*rt));
-       rt->flags = (unsigned int)rtm->rtm_flags;
-       COPYOUT6(rt->dest, rti_info[RTAX_DST]);
+
+       rt->rt_flags = (unsigned int)rtm->rtm_flags;
+       COPYSA(&rt->rt_dest, rti_info[RTAX_DST]);
        if (rtm->rtm_addrs & RTA_NETMASK)
-               COPYOUT6(rt->mask, rti_info[RTAX_NETMASK]);
-       else
-               ipv6_mask(&rt->mask, 128);
-       COPYOUT6(rt->gate, rti_info[RTAX_GATEWAY]);
-       rt->mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
+               COPYSA(&rt->rt_netmask, rti_info[RTAX_NETMASK]);
+       /* dhcpcd likes an unspecified gateway to indicate via the link. */
+       if (rt->rt_flags & RTF_GATEWAY &&
+           rti_info[RTAX_GATEWAY]->sa_family != AF_LINK)
+               COPYSA(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);
+       COPYSA(&rt->rt_ifa, rti_info[RTAX_SRC]);
+       rt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;
 
        if (rtm->rtm_index)
-               rt->iface = if_findindex(ctx->ifaces, rtm->rtm_index);
+               rt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
        else if (rtm->rtm_addrs & RTA_IFP)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_IFP]);
+               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
        else if (rtm->rtm_addrs & RTA_GATEWAY)
-               rt->iface = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
-
-       /* If we don't have an interface and it's a host route, it maybe
-        * to a local ip via the loopback interface. */
-       if (rt->iface == NULL &&
-           !(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
-       {
-               struct ipv6_addr *ia;
-
-               if ((ia = ipv6_findaddr(ctx, &rt->dest, 0)))
-                       rt->iface = ia->iface;
-       }
+               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
+       else
+               rt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);
 
-       if (rt->iface == NULL) {
+       if (rt->rt_ifp == NULL) {
                errno = ESRCH;
                return -1;
        }
 
-       if_finishrt6(rt);
-
        return 0;
 }
-#endif
 
 static int
 if_addrflags0(int fd, const char *ifname)
@@ -608,6 +545,7 @@ static void
 if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 {
        const struct sockaddr *sa;
+       struct rt rt;
 
        /* Ignore messages generated by us */
        if (rtm->rtm_pid == getpid()) {
@@ -632,21 +570,8 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
 
        sa = (const void *)(rtm + 1);
        switch (sa->sa_family) {
-#ifdef INET
-       case AF_INET:
-       {
-               struct rt rt;
-
-               if (if_copyrt(ctx, &rt, rtm) == 0)
-                       ipv4_handlert(ctx, rtm->rtm_type, &rt, 0);
-               break;
-       }
-#endif
 #ifdef INET6
        case AF_INET6:
-       {
-               struct rt6 rt6;
-
                if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY))
                        break;
                /*
@@ -670,54 +595,15 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
                        ipv6nd_neighbour(ctx, &dst6,
                            rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ?
                            IPV6ND_REACHABLE : 0);
-                       break;
                }
-
-               if (if_copyrt6(ctx, &rt6, rtm) == 0)
-                       ipv6_handlert(ctx, rtm->rtm_type, &rt6);
                break;
        }
 #endif
-       }
-}
-
-static int
-sa_cmp(const struct sockaddr *x, const struct sockaddr *y)
-{
-
-       if (x->sa_family != y->sa_family)
-               return x->sa_family < y->sa_family ? -1 : 1;
-       switch (x->sa_family) {
-#ifdef INET
-       case AF_INET:
-       {
-               const struct sockaddr_in *xin, *yin;
 
-               xin = (const void *)x;
-               yin = (const void *)y;
-               if (xin->sin_addr.s_addr != yin->sin_addr.s_addr)
-                       return xin->sin_addr.s_addr < yin->sin_addr.s_addr ?
-                           -1 : 1;
-               break;
+       if (if_copyrt(ctx, &rt, rtm) == 0) {
+               if_finishrt(&rt);
+               rt_recvrt(rtm->rtm_type, &rt);
        }
-#endif
-#ifdef INET6
-       case AF_INET6:
-       {
-               const struct sockaddr_in6 *xin6, *yin6;
-
-               xin6 = (const void *)x;
-               yin6 = (const void *)y;
-               return memcmp(xin6->sin6_addr.s6_addr,
-                   yin6->sin6_addr.s6_addr,
-                   sizeof(xin6->sin6_addr.s6_addr));
-       }
-#endif
-       default:
-               assert(!"unknown sa_family");
-       }
-
-       return 0;
 }
 
 static void
@@ -916,104 +802,6 @@ if_octetstr(char *buf, const Octet_t *o, ssize_t len)
        *p = '\0';
 }
 
-static int
-if_parsert(struct dhcpcd_ctx *ctx, unsigned int level, unsigned int name,
-    int (*walkrt)(struct dhcpcd_ctx *, char *, size_t))
-{
-       int                     s, retval, code, flags;
-       uintptr_t               buf[512 / sizeof(uintptr_t)];
-       struct strbuf           ctlbuf, databuf;
-       struct T_optmgmt_req    *tor = (struct T_optmgmt_req *)buf;
-       struct T_optmgmt_ack    *toa = (struct T_optmgmt_ack *)buf;
-       struct T_error_ack      *tea = (struct T_error_ack *)buf;
-       struct opthdr           *req;
-
-       if ((s = open("/dev/arp", O_RDWR)) == -1)
-               return -1;
-
-       /* Assume we are erroring. */
-       retval = -1;
-
-       tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
-       tor->OPT_offset = sizeof (struct T_optmgmt_req);
-       tor->OPT_length = sizeof (struct opthdr);
-       tor->MGMT_flags = T_CURRENT;
-
-       req = (struct opthdr *)&tor[1];
-       req->level = EXPER_IP_AND_ALL_IRES;
-       req->name = 0;
-       req->len = 1;
-
-       ctlbuf.buf = (char *)buf;
-       ctlbuf.len = tor->OPT_length + tor->OPT_offset;
-       if (putmsg(s, &ctlbuf, NULL, 0) == 1)
-               goto out;
-
-       req = (struct opthdr *)&toa[1];
-       ctlbuf.maxlen = sizeof(buf);
-
-       /* Create a reasonable buffer to start with */
-       databuf.maxlen = BUFSIZ * 2;
-       if ((databuf.buf = malloc(databuf.maxlen)) == NULL)
-               goto out;
-
-       for (;;) {
-               flags = 0;
-               if ((code = getmsg(s, &ctlbuf, 0, &flags)) == -1)
-                       break;
-               if (code == 0 &&
-                   toa->PRIM_type == T_OPTMGMT_ACK &&
-                   toa->MGMT_flags == T_SUCCESS &&
-                   (size_t)ctlbuf.len >= sizeof(struct T_optmgmt_ack))
-               {
-                       /* End of messages, so return success! */
-                       retval = 0;
-                       break;
-               }
-               if (tea->PRIM_type == T_ERROR_ACK) {
-                       errno = tea->TLI_error == TSYSERR ?
-                           tea->UNIX_error : EPROTO;
-                       break;
-               }
-               if (code != MOREDATA ||
-                   toa->PRIM_type != T_OPTMGMT_ACK ||
-                   toa->MGMT_flags != T_SUCCESS)
-               {
-                       errno = ENOMSG;
-                       break;
-               }
-
-               /* Try to ensure out buffer is big enough
-                * for future messages as well. */
-               if ((size_t)databuf.maxlen < req->len) {
-                       size_t newlen;
-
-                       free(databuf.buf);
-                       newlen = roundup(req->len, BUFSIZ);
-                       if ((databuf.buf = malloc(newlen)) == NULL)
-                               break;
-                       databuf.maxlen = newlen;
-               }
-
-               flags = 0;
-               if (getmsg(s, NULL, &databuf, &flags) == -1)
-                       break;
-
-               /* We always have to get the data before moving onto
-                * the next item, so don't move this test higher up
-                * to avoid the buffer allocation and getmsg calls. */
-               if (req->level == level && req->name == name) {
-                       if (walkrt(ctx, databuf.buf, req->len) == -1)
-                               break;
-               }
-       }
-
-       free(databuf.buf);
-out:
-       close(s);
-       return retval;
-}
-
 static int
 if_addaddr(int fd, const char *ifname,
     struct sockaddr_storage *addr, struct sockaddr_storage *mask)
@@ -1194,86 +982,323 @@ if_plumb(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)
                return if_unplumbif(ctx, af, ifname);
 }
 
-static int
-if_rtmsg(unsigned char cmd, const struct interface *ifp,
-    int addrs, int flags,
-    const struct sockaddr *dst, const struct sockaddr *mask,
-    const struct sockaddr *gate, const struct sockaddr *src,
-    uint32_t mtu)
+int
+if_route(unsigned char cmd, const struct rt *rt)
 {
+       struct dhcpcd_ctx *ctx;
        struct rtm
        {
                struct rt_msghdr hdr;
                char buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];
-       } rtm;
-       char *bp = rtm.buffer;
+       } rtmsg;
+       struct rt_msghdr *rtm;
+       char *bp = rtmsg.buffer;
        size_t l;
 
        /* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.
         * This includes subnet/prefix routes. */
 
+       ctx = rt->rt_ifp->ctx;
        if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) &&
-           ifp->ctx->options & DHCPCD_DAEMONISE &&
-           !(ifp->ctx->options & DHCPCD_DAEMONISED))
-               ifp->ctx->options |= DHCPCD_RTM_PPID;
+           ctx->options & DHCPCD_DAEMONISE &&
+           !(ctx->options & DHCPCD_DAEMONISED))
+               ctx->options |= DHCPCD_RTM_PPID;
 
-#define ADDSA(sa) {                                                          \
+#define ADDSA(sa) do {                                                       \
                l = RT_ROUNDUP(salen((sa)));                                  \
                memcpy(bp, (sa), l);                                          \
                bp += l;                                                      \
-       }
+       } while (/* CONSTCOND */ 0)
 
-       memset(&rtm, 0, sizeof(rtm));
-       rtm.hdr.rtm_version = RTM_VERSION;
-       rtm.hdr.rtm_type = cmd;
-       rtm.hdr.rtm_seq = ++ifp->ctx->seq;
-       rtm.hdr.rtm_flags = flags;
-       rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | addrs;
+       memset(&rtmsg, 0, sizeof(rtmsg));
+       rtm = &rtmsg.hdr;
+       rtm->rtm_version = RTM_VERSION;
+       rtm->rtm_type = cmd;
+       rtm->rtm_seq = ++ctx->seq;
+       rtm->rtm_flags = rt->rt_flags;
+       rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
 
        if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
-               rtm.hdr.rtm_flags |= RTF_UP;
-               if (!(rtm.hdr.rtm_flags & RTF_REJECT) ||
-                   !(rtm.hdr.rtm_flags & RTF_GATEWAY))
-                       rtm.hdr.rtm_addrs |= RTA_IFP;
-               if (mtu != 0) {
-                       rtm.hdr.rtm_inits |= RTV_MTU;
-                       rtm.hdr.rtm_rmx.rmx_mtu = mtu;
+               bool netmask_bcast = sa_is_allones(&rt->rt_netmask);
+
+               rtm->rtm_flags |= RTF_UP;
+               if (!(rtm->rtm_flags & RTF_REJECT) &&
+                   !sa_is_loopback(&rt->rt_gateway))
+               {
+                       rtm->rtm_addrs |= RTA_IFP;
+                       if (!sa_is_unspecified(&rt->rt_ifa))
+                               rtm->rtm_addrs |= RTA_IFA;
+               }
+               if (netmask_bcast)
+                       rtm->rtm_flags |= RTF_HOST;
+               else
+                       rtm->rtm_flags |= RTF_GATEWAY;
+
+               /* Emulate the kernel by marking address generated
+                * network routes non-static. */
+               if (!(rt->rt_dflags & RTDF_IFA_ROUTE))
+                       rtm->rtm_flags |= RTF_STATIC;
+
+               if (rt->rt_mtu != 0) {
+                       rtm->rtm_inits |= RTV_MTU;
+                       rtm->rtm_rmx.rmx_mtu = rt->rt_mtu;
                }
        }
 
-       ADDSA(dst);
-       ADDSA(gate);
-       if (rtm.hdr.rtm_addrs & RTA_NETMASK)
-               ADDSA(mask);
+       ADDSA(&rt->rt_dest);
+
+       if (sa_is_unspecified(&rt->rt_gateway))
+               ADDSA(&rt->rt_ifa);
+       else
+               ADDSA(&rt->rt_gateway);
+
+       if (rtm->rtm_addrs & RTA_NETMASK)
+               ADDSA(&rt->rt_netmask);
 
-       if (rtm.hdr.rtm_addrs & RTA_IFP) {
+       if (rtm->rtm_addrs & RTA_IFP) {
                struct sockaddr_dl sdl;
 
-               if_linkaddr(&sdl, ifp);
+               if_linkaddr(&sdl, rt->rt_ifp);
                ADDSA((struct sockaddr *)&sdl);
        }
 
 /* This no workie :/ */
-#if 0
+#if 1
        /* route(1M) says RTA_IFA is accepted but ignored
         * it's unclear how RTA_SRC is different. */
-       if (rtm.hdr.rtm_addrs & RTA_IFA) {
-               rtm.hdr.rtm_addrs &= ~RTA_IFA;
-               rtm.hdr.rtm_addrs |= RTA_SRC;
+       if (rtm->rtm_addrs & RTA_IFA) {
+               rtm->rtm_addrs &= ~RTA_IFA;
+               rtm->rtm_addrs |= RTA_SRC;
        }
-       if (rtm.hdr.rtm_addrs & RTA_SRC)
-               ADDSA(src);
+       if (rtm->rtm_addrs & RTA_SRC)
+               ADDSA(&rt->rt_ifa);
 #endif
 
 #undef ADDSA
 
-       rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
-       if (write(ifp->ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
+       rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);
+       if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1)
+               return -1;
+       ctx->sseq = ctx->seq;
+       return 0;
+}
+
+#ifdef INET
+static int
+if_walkrt(struct dhcpcd_ctx *ctx, char *data, size_t len)
+{
+       mib2_ipRouteEntry_t *re, *e;
+       struct rt rt;
+       char ifname[IF_NAMESIZE];
+       struct in_addr in;
+
+       if (len % sizeof(*re) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       re = (mib2_ipRouteEntry_t *)data;
+       e = (mib2_ipRouteEntry_t *)(data + len);
+       do {
+               /* Skip route types we don't want. */
+               switch (re->ipRouteInfo.re_ire_type) {
+               case IRE_IF_CLONE:
+               case IRE_BROADCAST:
+               case IRE_MULTICAST:
+               case IRE_NOROUTE:
+               case IRE_LOCAL:
+                       continue;
+               default:
+                       break;
+               }
+               memset(&rt, 0, sizeof(rt));
+               rt.rt_dflags |= RTDF_INIT;
+               in.s_addr = re->ipRouteDest;
+               sa_in_init(&rt.rt_dest, &in);
+               in.s_addr = re->ipRouteMask;
+               sa_in_init(&rt.rt_netmask, &in);
+               in.s_addr = re->ipRouteNextHop;
+               sa_in_init(&rt.rt_gateway, &in);
+               rt.rt_flags = re->ipRouteInfo.re_flags;
+               in.s_addr = re->ipRouteInfo.re_src_addr;
+               sa_in_init(&rt.rt_ifa, &in);
+               rt.rt_mtu = re->ipRouteInfo.re_max_frag;
+
+               if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));
+               rt.rt_ifp = if_find(ctx->ifaces, ifname);
+               if (rt.rt_ifp != NULL) {
+                       if_finishrt(&rt);
+                       rt_recvrt(RTM_ADD, &rt);
+               }
+       } while (++re < e);
+       return 0;
+}
+#endif
+
+#ifdef INET6
+static int
+if_walkrt6(struct dhcpcd_ctx *ctx, char *data, size_t len)
+{
+       mib2_ipv6RouteEntry_t *re, *e;
+       struct rt rt;
+       char ifname[IF_NAMESIZE];
+       struct in6_addr in6;
+
+       if (len % sizeof(*re) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       re = (mib2_ipv6RouteEntry_t *)data;
+       e = (mib2_ipv6RouteEntry_t *)(data + len);
+
+       do {
+               /* Skip route types we don't want. */
+               switch (re->ipv6RouteInfo.re_ire_type) {
+               case IRE_IF_CLONE:
+               case IRE_BROADCAST:
+               case IRE_MULTICAST:
+               case IRE_NOROUTE:
+               case IRE_LOCAL:
+                       continue;
+               default:
+                       break;
+               }
+               memset(&rt, 0, sizeof(rt));
+               rt.rt_dflags |= RTDF_INIT;
+               sa_in6_init(&rt.rt_dest, &re->ipv6RouteDest);
+               ipv6_mask(&in6, re->ipv6RoutePfxLength);
+               sa_in6_init(&rt.rt_netmask, &in6);
+               sa_in6_init(&rt.rt_gateway, &re->ipv6RouteNextHop);
+               rt.rt_mtu = re->ipv6RouteInfo.re_max_frag;
+
+               if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));
+               rt.rt_ifp = if_find(ctx->ifaces, ifname);
+               if (rt.rt_ifp != NULL) {
+                       if_finishrt(&rt);
+                       rt_recvrt(RTM_ADD, &rt);
+               }
+       } while (++re < e);
+       return 0;
+}
+#endif
+
+static int
+if_parsert(struct dhcpcd_ctx *ctx, unsigned int level, unsigned int name,
+    int (*walkrt)(struct dhcpcd_ctx *, char *, size_t))
+{
+       int                     s, retval, code, flags;
+       uintptr_t               buf[512 / sizeof(uintptr_t)];
+       struct strbuf           ctlbuf, databuf;
+       struct T_optmgmt_req    *tor = (struct T_optmgmt_req *)buf;
+       struct T_optmgmt_ack    *toa = (struct T_optmgmt_ack *)buf;
+       struct T_error_ack      *tea = (struct T_error_ack *)buf;
+       struct opthdr           *req;
+
+       if ((s = open("/dev/arp", O_RDWR)) == -1)
+               return -1;
+
+       /* Assume we are erroring. */
+       retval = -1;
+
+       tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+       tor->OPT_offset = sizeof (struct T_optmgmt_req);
+       tor->OPT_length = sizeof (struct opthdr);
+       tor->MGMT_flags = T_CURRENT;
+
+       req = (struct opthdr *)&tor[1];
+       req->level = EXPER_IP_AND_ALL_IRES;
+       req->name = 0;
+       req->len = 1;
+
+       ctlbuf.buf = (char *)buf;
+       ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+       if (putmsg(s, &ctlbuf, NULL, 0) == 1)
+               goto out;
+
+       req = (struct opthdr *)&toa[1];
+       ctlbuf.maxlen = sizeof(buf);
+
+       /* Create a reasonable buffer to start with */
+       databuf.maxlen = BUFSIZ * 2;
+       if ((databuf.buf = malloc(databuf.maxlen)) == NULL)
+               goto out;
+
+       for (;;) {
+               flags = 0;
+               if ((code = getmsg(s, &ctlbuf, 0, &flags)) == -1)
+                       break;
+               if (code == 0 &&
+                   toa->PRIM_type == T_OPTMGMT_ACK &&
+                   toa->MGMT_flags == T_SUCCESS &&
+                   (size_t)ctlbuf.len >= sizeof(struct T_optmgmt_ack))
+               {
+                       /* End of messages, so return success! */
+                       retval = 0;
+                       break;
+               }
+               if (tea->PRIM_type == T_ERROR_ACK) {
+                       errno = tea->TLI_error == TSYSERR ?
+                           tea->UNIX_error : EPROTO;
+                       break;
+               }
+               if (code != MOREDATA ||
+                   toa->PRIM_type != T_OPTMGMT_ACK ||
+                   toa->MGMT_flags != T_SUCCESS)
+               {
+                       errno = ENOMSG;
+                       break;
+               }
+
+               /* Try to ensure out buffer is big enough
+                * for future messages as well. */
+               if ((size_t)databuf.maxlen < req->len) {
+                       size_t newlen;
+
+                       free(databuf.buf);
+                       newlen = roundup(req->len, BUFSIZ);
+                       if ((databuf.buf = malloc(newlen)) == NULL)
+                               break;
+                       databuf.maxlen = newlen;
+               }
+
+               flags = 0;
+               if (getmsg(s, NULL, &databuf, &flags) == -1)
+                       break;
+
+               /* We always have to get the data before moving onto
+                * the next item, so don't move this test higher up
+                * to avoid the buffer allocation and getmsg calls. */
+               if (req->level == level && req->name == name) {
+                       if (walkrt(ctx, databuf.buf, req->len) == -1)
+                               break;
+               }
+       }
+
+       free(databuf.buf);
+out:
+       close(s);
+       return retval;
+}
+
+
+int
+if_initrt(struct dhcpcd_ctx *ctx)
+{
+
+       rt_headclear(&ctx->kroutes);
+#ifdef INET
+       if (if_parsert(ctx, MIB2_IP,MIB2_IP_ROUTE, if_walkrt) == -1)
+               return -1;
+#endif
+#ifdef INET6
+       if (if_parsert(ctx, MIB2_IP6, MIB2_IP6_ROUTE, if_walkrt6) == -1)
                return -1;
-       ifp->ctx->sseq = ifp->ctx->seq;
+#endif
        return 0;
 }
 
+
 #ifdef INET
 const char *if_pfname = "DLPI";
 
@@ -1476,132 +1501,6 @@ if_addrflags(const struct interface *ifp, __unused const struct in_addr *addr,
        return flags;
 }
 
-int
-if_route(unsigned char cmd, const struct rt *rt)
-{
-       struct sockaddr_in      dst = {
-               .sin_family = AF_INET,
-               .sin_addr = rt->dest
-       };
-       struct sockaddr_in      mask = {
-               .sin_family = AF_INET,
-               .sin_addr = rt->mask
-       };
-       struct sockaddr_in      gate = {
-               .sin_family = AF_INET,
-               .sin_addr = rt->gate
-       };
-       struct sockaddr_in      src = {
-               .sin_family = AF_INET,
-               .sin_addr = rt->src
-       };
-       struct sockaddr_in      *g;
-       int                     addrs, flags;
-
-       addrs = 0;
-       flags = 0;
-
-       if (cmd == RTM_ADD || cmd == RTM_CHANGE) {
-               addrs |= RTA_GATEWAY | RTA_IFP;
-               /* Subnet routes are cloning or connected if supported.
-                * All other routes are static. */
-               if (rt->gate.s_addr != ntohl(INADDR_ANY))
-                       flags |= RTF_STATIC;
-               if (rt->src.s_addr != ntohl(INADDR_ANY))
-                       addrs |= RTA_IFA;
-       }
-
-       if (rt->mask.s_addr == htonl(INADDR_BROADCAST) &&
-           rt->gate.s_addr == htonl(INADDR_ANY))
-       {
-               flags |= RTF_HOST;
-       } else if (rt->gate.s_addr == htonl(INADDR_LOOPBACK) &&
-           rt->mask.s_addr == htonl(INADDR_BROADCAST))
-       {
-               flags |= RTF_HOST | RTF_GATEWAY;
-               /* Going via lo0 so remove the interface flags */
-               addrs &= ~(RTA_IFA | RTA_IFP);
-       } else {
-               addrs |= RTA_NETMASK;
-               if (flags & RTF_STATIC)
-                       flags |= RTF_GATEWAY;
-               if (rt->mask.s_addr == htonl(INADDR_BROADCAST))
-                       flags |= RTF_HOST;
-       }
-
-       if ((flags & RTF_HOST && rt->gate.s_addr == htonl(INADDR_ANY)) ||
-           !(flags & RTF_STATIC))
-               g = &src;
-       else
-               g = &gate;
-       return if_rtmsg(cmd, rt->iface, addrs, flags,
-           (struct sockaddr *)&dst, (struct sockaddr *)&mask,
-           (struct sockaddr *)g, (struct sockaddr *)&src, rt->mtu);
-}
-
-static int
-if_walkrt(struct dhcpcd_ctx *ctx, char *data, size_t len)
-{
-       mib2_ipRouteEntry_t *re, *e;
-       struct rt rt;
-       char ifname[IF_NAMESIZE];
-
-       if (len % sizeof(*re) != 0) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       re = (mib2_ipRouteEntry_t *)data;
-       e = (mib2_ipRouteEntry_t *)(data + len);
-       do {
-               /* Skip route types we don't want. */
-               switch (re->ipRouteInfo.re_ire_type) {
-               case IRE_IF_CLONE:
-               case IRE_BROADCAST:
-               case IRE_MULTICAST:
-               case IRE_NOROUTE:
-               case IRE_LOCAL:
-                       continue;
-               default:
-                       break;
-               }
-               memset(&rt, 0, sizeof(rt));
-               rt.dest.s_addr = re->ipRouteDest;
-               rt.mask.s_addr = re->ipRouteMask;
-               rt.gate.s_addr = re->ipRouteNextHop;
-               rt.flags = re->ipRouteInfo.re_flags;
-               rt.src.s_addr = re->ipRouteInfo.re_src_addr;
-               rt.mtu = re->ipRouteInfo.re_max_frag;
-
-               if_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));
-               rt.iface = if_find(ctx->ifaces, ifname);
-               if (rt.iface != NULL) {
-                       if_finishrt(&rt);
-                       ipv4_handlert(ctx, RTM_ADD, &rt, 1);
-               } else {
-                       char            destbuf[INET6_ADDRSTRLEN];
-                       char            gatebuf[INET6_ADDRSTRLEN];
-                       const char      *dest, *gate;
-
-                       dest = inet_ntop(AF_INET, &rt.dest,
-                           destbuf, INET6_ADDRSTRLEN);
-                       gate = inet_ntop(AF_INET, &rt.gate,
-                           gatebuf, INET6_ADDRSTRLEN);
-                       logger(ctx, LOG_ERR,
-                           "no iface (%s) for route to %s via %s",
-                           ifname, dest, gate);
-               }
-       } while (++re < e);
-       return 0;
-}
-
-int
-if_initrt(struct dhcpcd_ctx *ctx)
-{
-
-       ipv4_freerts(ctx->ipv4_kroutes);
-       return if_parsert(ctx, MIB2_IP,MIB2_IP_ROUTE, if_walkrt);
-}
 #endif
 
 #ifdef INET6
@@ -1663,109 +1562,6 @@ if_getlifetime6(struct ipv6_addr *addr)
        return -1;
 }
 
-int
-if_route6(unsigned char cmd, const struct rt6 *rt)
-{
-       struct sockaddr_in6     dst = {
-               .sin6_family = AF_INET6,
-               .sin6_addr = rt->dest
-       };
-       struct sockaddr_in6     mask = {
-               .sin6_family = AF_INET6,
-               .sin6_addr = rt->mask
-       };
-       struct sockaddr_in6     gate = {
-               .sin6_family = AF_INET6,
-               .sin6_addr = rt->gate
-       };
-       struct sockaddr_in6     src = {
-               .sin6_family = AF_INET6,
-               .sin6_addr = rt->src
-       };
-       struct sockaddr_in6     *g;
-       int                     addrs, flags;
-
-       addrs = RTA_NETMASK;
-       flags = 0;
-
-       if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate)) {
-               /* XXX FIXME: How to tell kernel route is subnet? */
-       } else
-               flags |= RTF_GATEWAY | RTF_STATIC;
-
-       if (IN6_IS_ADDR_UNSPECIFIED(&rt->gate))
-               g = &src;
-       else
-               g = &gate;
-
-       return if_rtmsg(cmd, rt->iface, addrs, flags,
-           (struct sockaddr *)&dst, (struct sockaddr *)&mask,
-           (struct sockaddr *)g, (struct sockaddr *)&src, rt->mtu);
-}
-
-static int
-if_walkrt6(struct dhcpcd_ctx *ctx, char *data, size_t len)
-{
-       mib2_ipv6RouteEntry_t *re, *e;
-       struct rt6 rt;
-       char ifname[IF_NAMESIZE];
-
-       if (len % sizeof(*re) != 0) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       re = (mib2_ipv6RouteEntry_t *)data;
-       e = (mib2_ipv6RouteEntry_t *)(data + len);
-
-       do {
-               /* Skip route types we don't want. */
-               switch (re->ipv6RouteInfo.re_ire_type) {
-               case IRE_IF_CLONE:
-               case IRE_BROADCAST:
-               case IRE_MULTICAST:
-               case IRE_NOROUTE:
-               case IRE_LOCAL:
-                       continue;
-               default:
-                       break;
-               }
-               memset(&rt, 0, sizeof(rt));
-               rt.dest = re->ipv6RouteDest;
-               ipv6_mask(&rt.mask, re->ipv6RoutePfxLength);
-               rt.gate = re->ipv6RouteNextHop;
-               rt.mtu = re->ipv6RouteInfo.re_max_frag;
-
-               if_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));
-               rt.iface = if_find(ctx->ifaces, ifname);
-               if (rt.iface != NULL) {
-                       if_finishrt6(&rt);
-                       ipv6_handlert(ctx, RTM_ADD, &rt);
-               } else {
-                       char destbuf[INET6_ADDRSTRLEN];
-                       char gatebuf[INET6_ADDRSTRLEN];
-                       const char *dest, *gate;
-
-                       dest = inet_ntop(AF_INET6, &rt.dest,
-                           destbuf, INET6_ADDRSTRLEN);
-                       gate = inet_ntop(AF_INET6, &rt.gate,
-                           gatebuf, INET6_ADDRSTRLEN);
-                       logger(ctx, LOG_ERR,
-                           "no iface (%s) for route to %s via %s",
-                           ifname, dest, gate);
-               }
-       } while (++re < e);
-       return 0;
-}
-
-int
-if_initrt6(struct dhcpcd_ctx *ctx)
-{
-
-       ipv6_freerts(&ctx->ipv6->kroutes);
-       return if_parsert(ctx, MIB2_IP6, MIB2_IP6_ROUTE, if_walkrt6);
-}
-
 int
 if_checkipv6(__unused struct dhcpcd_ctx *ctx,
     __unused const struct interface *ifp, int __unused own)
diff --git a/if.c b/if.c
index fcfdf3da04abe5829752962839a609a373ffa435..ac221f71439d30c1446dc19f1468f3aec71e4a02 100644 (file)
--- a/if.c
+++ b/if.c
@@ -82,6 +82,7 @@ if_free(struct interface *ifp)
        dhcp6_free(ifp);
        ipv6nd_free(ifp);
        ipv6_free(ifp);
+       rt_freeif(ifp);
        free_options(ifp->options);
        free(ifp);
 }
@@ -454,9 +455,6 @@ if_discover(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
                                break;
                        }
                        ifp->hwlen = sdl->sdl_alen;
-#ifndef CLLADDR
-#  define CLLADDR(s) (const void *)((s)->sdl_data + (s)->sdl_nlen)
-#endif
                        memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
 #elif AF_PACKET
                        sll = (const void *)ifa->ifa_addr;
@@ -633,6 +631,18 @@ if_findindex(struct if_head *ifaces, unsigned int idx)
        return if_findindexname(ifaces, idx, NULL);
 }
 
+struct interface *
+if_loopback(struct dhcpcd_ctx *ctx)
+{
+       struct interface *ifp;
+
+       TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+               if (ifp->flags & IFF_LOOPBACK)
+                       return ifp;
+       }
+       return NULL;
+}
+
 int
 if_domtu(const struct interface *ifp, short int mtu)
 {
diff --git a/if.h b/if.h
index d2b757aef65cf2f27116ed6aacab6924f794a463..a8555a434719544e993787e089d807d09b5f239c 100644 (file)
--- a/if.h
+++ b/if.h
 #include <netinet/in_var.h>    /* for IN_IFF_TENTATIVE et all */
 #endif
 
-/* Some systems have route metrics.
- * OpenBSD route priority is not this. */
-#ifndef HAVE_ROUTE_METRIC
-# if defined(__linux__)
-#  define HAVE_ROUTE_METRIC 1
-# endif
-#endif
-
-#if defined(__OpenBSD__) || defined (__sun)
-#  define ROUTE_PER_GATEWAY
-/* XXX dhcpcd doesn't really support this yet.
- * But that's generally OK if only dhcpcd is managing routes. */
-#endif
-
 /* Some systems have in-built IPv4 DAD.
  * However, we need them to do DAD at carrier up as well. */
 #ifdef IN_IFF_TENTATIVE
@@ -76,6 +62,7 @@
 #include "dhcpcd.h"
 #include "ipv4.h"
 #include "ipv6.h"
+#include "route.h"
 
 #define EUI64_ADDR_LEN                 8
 #define INFINIBAND_ADDR_LEN            20
 #define RAW_EOF                        1 << 0
 #define RAW_PARTIALCSUM                2 << 0
 
+#ifndef CLLADDR
+#ifdef AF_LINK
+#  define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
+#endif
+#endif
+
 #ifdef __sun
 /* Solaris stupidly defines this for compat with BSD
  * but then ignores it. */
@@ -121,6 +114,7 @@ int if_setflag(struct interface *ifp, short flag);
 struct if_head *if_discover(struct dhcpcd_ctx *, int, char * const *);
 struct interface *if_find(struct if_head *, const char *);
 struct interface *if_findindex(struct if_head *, unsigned int);
+struct interface *if_loopback(struct dhcpcd_ctx *);
 void if_sortinterfaces(struct dhcpcd_ctx *);
 void if_free(struct interface *);
 int if_domtu(const struct interface *, short int);
@@ -207,8 +201,6 @@ int if_addrflags6(const struct interface *, const struct in6_addr *,
     const char *);
 int if_getlifetime6(struct ipv6_addr *);
 
-int if_route6(unsigned char, const struct rt6 *rt);
-int if_initrt6(struct dhcpcd_ctx *);
 #else
 #define if_checkipv6(a, b, c) (-1)
 #endif
diff --git a/ipv4.c b/ipv4.c
index 876ca0afc185c97b21f5f2040e9b12b19c4c2403..2f64f38229f207031d100d8c7797aa8aedb19ff4 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -51,7 +51,9 @@
 #include "if-options.h"
 #include "ipv4.h"
 #include "ipv4ll.h"
+#include "route.h"
 #include "script.h"
+#include "sa.h"
 
 #define IPV4_LOOPBACK_ROUTE
 #if defined(__linux__) || defined(__sun) || (defined(BSD) && defined(RTF_LOCAL))
@@ -206,35 +208,6 @@ ipv4_hasaddr(const struct interface *ifp)
            dstate->addr != NULL);
 }
 
-void
-ipv4_freeroutes(struct rt_head *rts)
-{
-
-       if (rts) {
-               ipv4_freerts(rts);
-               free(rts);
-       }
-}
-
-int
-ipv4_init(struct dhcpcd_ctx *ctx)
-{
-
-       if (ctx->ipv4_routes == NULL) {
-               ctx->ipv4_routes = malloc(sizeof(*ctx->ipv4_routes));
-               if (ctx->ipv4_routes == NULL)
-                       return -1;
-               TAILQ_INIT(ctx->ipv4_routes);
-       }
-       if (ctx->ipv4_kroutes == NULL) {
-               ctx->ipv4_kroutes = malloc(sizeof(*ctx->ipv4_kroutes));
-               if (ctx->ipv4_kroutes == NULL)
-                       return -1;
-               TAILQ_INIT(ctx->ipv4_kroutes);
-       }
-       return 0;
-}
-
 /* Interface comparer for working out ordering. */
 int
 ipv4_ifcmp(const struct interface *si, const struct interface *ti)
@@ -270,427 +243,126 @@ ipv4_ifcmp(const struct interface *si, const struct interface *ti)
        return 0;
 }
 
-static struct rt *
-find_route(struct rt_head *rts, const struct rt *r, const struct rt *srt)
-{
-       struct rt *rt;
-
-       if (rts == NULL)
-               return NULL;
-       TAILQ_FOREACH(rt, rts, next) {
-               if (rt->dest.s_addr == r->dest.s_addr &&
-#ifdef HAVE_ROUTE_METRIC
-                   (srt || (r->iface == NULL || rt->iface == NULL ||
-                   rt->iface->metric == r->iface->metric)) &&
-#endif
-                    (!srt || srt != rt) &&
-                   rt->mask.s_addr == r->mask.s_addr)
-                       return rt;
-       }
-       return NULL;
-}
-
-static void
-desc_route(const char *cmd, const struct rt *rt)
-{
-       char addr[INET_ADDRSTRLEN];
-       struct dhcpcd_ctx *ctx = rt->iface ? rt->iface->ctx : NULL;
-       const char *ifname = rt->iface ? rt->iface->name : NULL;
-
-       strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr));
-       if (rt->mask.s_addr == htonl(INADDR_BROADCAST) &&
-           rt->gate.s_addr == htonl(INADDR_ANY))
-               logger(ctx, LOG_INFO, "%s: %s host route to %s",
-                   ifname, cmd, addr);
-       else if (rt->mask.s_addr == htonl(INADDR_BROADCAST))
-               logger(ctx, LOG_INFO, "%s: %s host route to %s via %s",
-                   ifname, cmd, addr, inet_ntoa(rt->gate));
-       else if (rt->dest.s_addr == htonl(INADDR_ANY) &&
-           rt->mask.s_addr == htonl(INADDR_ANY) &&
-           rt->gate.s_addr == htonl(INADDR_ANY))
-               logger(ctx, LOG_INFO, "%s: %s default route",
-                   ifname, cmd);
-       else if (rt->gate.s_addr == htonl(INADDR_ANY))
-               logger(ctx, LOG_INFO, "%s: %s route to %s/%d",
-                   ifname, cmd, addr, inet_ntocidr(rt->mask));
-       else if (rt->dest.s_addr == htonl(INADDR_ANY) &&
-           rt->mask.s_addr == htonl(INADDR_ANY))
-               logger(ctx, LOG_INFO, "%s: %s default route via %s",
-                   ifname, cmd, inet_ntoa(rt->gate));
-       else
-               logger(ctx, LOG_INFO, "%s: %s route to %s/%d via %s",
-                   ifname, cmd, addr, inet_ntocidr(rt->mask),
-                   inet_ntoa(rt->gate));
-}
-
-static struct rt *
-ipv4_findrt(struct dhcpcd_ctx *ctx, const struct rt *rt, int flags)
-{
-       struct rt *r;
-
-       if (ctx->ipv4_kroutes == NULL)
-               return NULL;
-       TAILQ_FOREACH(r, ctx->ipv4_kroutes, next) {
-               if (rt->dest.s_addr == r->dest.s_addr &&
-#ifdef HAVE_ROUTE_METRIC
-                   (rt->iface == NULL || rt->iface == r->iface) &&
-                   (!flags || rt->metric == r->metric) &&
-#else
-                   (!flags || rt->iface == r->iface) &&
-#endif
-                   rt->mask.s_addr == r->mask.s_addr)
-                       return r;
-       }
-       return NULL;
-}
-
-void
-ipv4_freerts(struct rt_head *routes)
-{
-       struct rt *rt;
-
-       while ((rt = TAILQ_FIRST(routes))) {
-               TAILQ_REMOVE(routes, rt, next);
-               free(rt);
-       }
-}
-
-/* If something other than dhcpcd removes a route,
- * we need to remove it from our internal table. */
-int
-ipv4_handlert(struct dhcpcd_ctx *ctx, int cmd, const struct rt *rt, int flags)
-{
-       struct rt *f;
-
-       if (ctx->ipv4_kroutes == NULL)
-               return 0;
-
-       f = ipv4_findrt(ctx, rt, 1);
-       switch (cmd) {
-       case RTM_ADD:
-               if (f == NULL) {
-                       if ((f = malloc(sizeof(*f))) == NULL)
-                               return -1;
-                       *f = *rt;
-                       TAILQ_INSERT_TAIL(ctx->ipv4_kroutes, f, next);
-               }
-               break;
-       case RTM_DELETE:
-               if (f) {
-                       TAILQ_REMOVE(ctx->ipv4_kroutes, f, next);
-                       free(f);
-               }
-
-               /* If we manage the route, remove it */
-               if ((f = find_route(ctx->ipv4_routes, rt, NULL))) {
-                       desc_route("deleted", f);
-                       TAILQ_REMOVE(ctx->ipv4_routes, f, next);
-                       free(f);
-               }
-               break;
-       }
-
-       return flags ? 0 : ipv4ll_handlert(ctx, cmd, rt);
-}
-
-static void
-d_kroute(struct rt *rt)
-{
-       struct dhcpcd_ctx *ctx;
-
-       ctx = rt->iface->ctx;
-       rt = ipv4_findrt(ctx, rt, 1);
-       if (rt != NULL) {
-               TAILQ_REMOVE(ctx->ipv4_kroutes, rt, next);
-               free(rt);
-       }
-}
-
-#define n_route(a)      nc_route(NULL, a)
-#define c_route(a, b)   nc_route(a, b)
 static int
-nc_route(struct rt *ort, struct rt *nrt)
+inet_dhcproutes(struct rt_head *routes, struct interface *ifp)
 {
-       int change;
-
-       /* Don't set default routes if not asked to */
-       if (nrt->dest.s_addr == 0 &&
-           nrt->mask.s_addr == 0 &&
-           !(nrt->iface->options->options & DHCPCD_GATEWAY))
-               return -1;
-
-       desc_route(ort == NULL ? "adding" : "changing", nrt);
-
-       change = 0;
-       if (ort == NULL) {
-               ort = ipv4_findrt(nrt->iface->ctx, nrt, 0);
-               if (ort &&
-                   ((ort->flags & RTF_REJECT && nrt->flags & RTF_REJECT) ||
-                    (ort->iface == nrt->iface &&
-#ifdef HAVE_ROUTE_METRIC
-                   ort->metric == nrt->metric &&
-#endif
-                   ort->gate.s_addr == nrt->gate.s_addr)))
-               {
-                       if (ort->mtu == nrt->mtu)
-                               return 0;
-                       change = 1;
-               }
-       } else if (ort->state & STATE_FAKE && !(nrt->state & STATE_FAKE) &&
-           ort->iface == nrt->iface &&
-#ifdef HAVE_ROUTE_METRIC
-           ort->metric == nrt->metric &&
-#endif
-           ort->dest.s_addr == nrt->dest.s_addr &&
-           ort->mask.s_addr ==  nrt->mask.s_addr &&
-           ort->gate.s_addr == nrt->gate.s_addr)
-       {
-               if (ort->mtu == nrt->mtu)
-                       return 0;
-               change = 1;
-       }
-
-#ifdef RTF_CLONING
-       /* BSD can set routes to be cloning routes.
-        * Cloned routes inherit the parent flags.
-        * As such, we need to delete and re-add the route to flush children
-        * to correct the flags. */
-       if (change && ort != NULL && ort->flags & RTF_CLONING)
-               change = 0;
-#endif
-
-       if (change) {
-               if (if_route(RTM_CHANGE, nrt) != -1)
-                       return 0;
-               if (errno != ESRCH)
-                       logger(nrt->iface->ctx, LOG_ERR, "if_route (CHG): %m");
-       }
+       const struct dhcp_state *state;
+       struct rt_head nroutes;
+       struct rt *rt, *r = NULL;
+       struct in_addr in;
+       uint16_t mtu;
+       int n;
 
-#ifdef HAVE_ROUTE_METRIC
-       /* With route metrics, we can safely add the new route before
-        * deleting the old route. */
-       if (if_route(RTM_ADD, nrt) != -1) {
-               if (ort && if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
-                       logger(nrt->iface->ctx, LOG_ERR, "if_route (DEL): %m");
+       state = D_CSTATE(ifp);
+       if (state == NULL || state->state != DHS_BOUND)
                return 0;
-       }
 
-       /* If the kernel claims the route exists we need to rip out the
-        * old one first. */
-       if (errno != EEXIST || ort == NULL)
-               goto logerr;
-#endif
+       TAILQ_INIT(&nroutes);
 
-       /* No route metrics, we need to delete the old route before
-        * adding the new one. */
-#ifdef ROUTE_PER_GATEWAY
-       errno = 0;
+       /* First, add a subnet route. */
+       if (!(ifp->flags & IFF_POINTOPOINT) &&
+#ifndef BSD
+               /* BSD adds a route in this instance */
+           state->addr->mask.s_addr != INADDR_BROADCAST &&
 #endif
-       if (ort) {
-               if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
-                       logger(nrt->iface->ctx, LOG_ERR, "if_route (DEL): %m");
-               else
-                       d_kroute(ort);
-       }
-#ifdef ROUTE_PER_GATEWAY
-       /* The OS allows many routes to the same dest with different gateways.
-        * dhcpcd does not support this yet, so for the time being just keep on
-        * deleting the route until there is an error. */
-       if (ort && errno == 0) {
-               for (;;) {
-                       if (if_route(RTM_DELETE, ort) == -1)
+           state->addr->mask.s_addr != INADDR_ANY)
+       {
+               if ((rt = rt_new(ifp)) == NULL)
+                       return -1;
+               rt->rt_dflags |= RTDF_IFA_ROUTE;
+               in.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
+               sa_in_init(&rt->rt_dest, &in);
+               in.s_addr = state->addr->mask.s_addr;
+               sa_in_init(&rt->rt_netmask, &in);
+               //in.s_addr = INADDR_ANY;
+               //sa_in_init(&rt->rt_gateway, &in);
+               rt->rt_gateway.sa_family = AF_UNSPEC;
+               TAILQ_INSERT_HEAD(&nroutes, rt, rt_next);
+       }
+
+       /* If any set routes, grab them, otherwise DHCP routes. */
+       if (TAILQ_FIRST(&ifp->options->routes)) {
+               TAILQ_FOREACH(r, &ifp->options->routes, rt_next) {
+                       if (sa_is_unspecified(&r->rt_gateway))
                                break;
+                       if ((rt = rt_new(ifp)) == NULL)
+                               return -1;
+                       memcpy(rt, r, sizeof(*rt));
+                       TAILQ_INSERT_TAIL(&nroutes, rt, rt_next);
                }
+       } else {
+               if (dhcp_get_routes(&nroutes, ifp) == -1)
+                       return -1;
        }
-#endif
-       if (if_route(RTM_ADD, nrt) != -1)
-               return 0;
-#ifdef HAVE_ROUTE_METRIC
-logerr:
-#endif
-       logger(nrt->iface->ctx, LOG_ERR, "if_route (ADD): %m");
-       return -1;
-}
-
-static int
-d_route(struct rt *rt)
-{
-       int retval;
-
-       desc_route("deleting", rt);
-       retval = if_route(RTM_DELETE, rt) == -1 ? -1 : 0;
-       if (retval == -1 && errno != ENOENT && errno != ESRCH)
-               logger(rt->iface->ctx, LOG_ERR,
-                   "%s: if_delroute: %m", rt->iface->name);
-       /* Remove the route from our kernel table so we can add a
-        * IPv4LL default route if possible. */
-       else
-               d_kroute(rt);
-       return retval;
-}
-
-static struct rt_head *
-add_subnet_route(struct rt_head *rt, const struct interface *ifp)
-{
-       const struct dhcp_state *state;
-       struct rt *r;
-
-       if (rt == NULL) /* earlier malloc failed */
-               return NULL;
-
-       /* P2P interfaces don't have subnet routes as such. */
-       if (ifp->flags & IFF_POINTOPOINT)
-               return rt;
-
-       state = D_CSTATE(ifp);
-       /* Don't create a subnet route for these addresses */
-       if (state->addr->mask.s_addr == INADDR_ANY)
-               return rt;
-#ifndef BSD
-       /* BSD adds a route in this instance */
-       if (state->addr->mask.s_addr == INADDR_BROADCAST)
-               return rt;
-#endif
-
-       if ((r = calloc(1, sizeof(*r))) == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-               ipv4_freeroutes(rt);
-               return NULL;
-       }
-       r->dest.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
-       r->mask.s_addr = state->addr->mask.s_addr;
-       r->gate.s_addr = INADDR_ANY;
-       r->mtu = dhcp_get_mtu(ifp);
-       r->src = state->addr->addr;
-
-       TAILQ_INSERT_HEAD(rt, r, next);
-       return rt;
-}
-
-#ifdef IPV4_LOOPBACK_ROUTE
-static struct rt_head *
-add_loopback_route(struct rt_head *rt, const struct interface *ifp)
-{
-       struct rt *r;
-       const struct dhcp_state *state;
-
-       if (rt == NULL) /* earlier malloc failed */
-               return NULL;
 
-       state = D_CSTATE(ifp);
-       if (state->addr == NULL)
-               return rt;
-
-       if ((r = calloc(1, sizeof(*r))) == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-               ipv4_freeroutes(rt);
-               return NULL;
+       /* If configured, Install a gateway to the desintion
+        * for P2P interfaces. */
+       if (ifp->flags & IFF_POINTOPOINT &&
+           ifp->options->options & DHCPCD_GATEWAY &&
+           has_option_mask(ifp->options->dstmask, DHO_ROUTER))
+       {
+               if ((rt = rt_new(ifp)) == NULL)
+                       return -1;
+               in.s_addr = INADDR_ANY;
+               sa_in_init(&rt->rt_dest, &in);
+               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);
+               TAILQ_INSERT_HEAD(routes, rt, rt_next);
        }
-       r->dest = state->addr->addr;
-       r->mask.s_addr = INADDR_BROADCAST;
-       r->gate.s_addr = htonl(INADDR_LOOPBACK);
-       r->mtu = dhcp_get_mtu(ifp);
-       r->src = state->addr->addr;
-       TAILQ_INSERT_HEAD(rt, r, next);
-       return rt;
-}
-#endif
-
-static struct rt_head *
-get_routes(struct interface *ifp)
-{
-       struct rt_head *nrt;
-       struct rt *rt, *r = NULL;
-       const struct dhcp_state *state;
 
-       if (ifp->options->routes && TAILQ_FIRST(ifp->options->routes)) {
-               if ((nrt = malloc(sizeof(*nrt))) == NULL)
-                       return NULL;
-               TAILQ_INIT(nrt);
-               TAILQ_FOREACH(rt, ifp->options->routes, next) {
-                       if (rt->gate.s_addr == 0)
-                               break;
-                       if ((r = calloc(1, sizeof(*r))) == NULL) {
-                               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                               ipv4_freeroutes(nrt);
-                               return NULL;
-                       }
-                       memcpy(r, rt, sizeof(*r));
-                       TAILQ_INSERT_TAIL(nrt, r, next);
-               }
-       } else
-               nrt = dhcp_get_routes(ifp);
-
-       /* Copy our address as the source address */
-       if (nrt) {
-               state = D_CSTATE(ifp);
-               TAILQ_FOREACH(rt, nrt, next) {
-                       rt->src = state->addr->addr;
-               }
+       /* Copy our address as the source address and set mtu */
+       mtu = dhcp_get_mtu(ifp);
+       n = 0;
+       TAILQ_FOREACH(rt, &nroutes, rt_next) {
+               rt->rt_mtu = mtu;
+               sa_in_init(&rt->rt_ifa, &state->addr->addr);
+               n++;
        }
+       TAILQ_CONCAT(routes, &nroutes, rt_next);
 
-       return nrt;
-}
-
-static struct rt_head *
-add_destination_route(struct rt_head *rt, const struct interface *ifp)
-{
-       struct rt *r;
-       const struct dhcp_state *state;
-
-       if (rt == NULL || /* failed a malloc earlier probably */
-           !(ifp->flags & IFF_POINTOPOINT) ||
-           !has_option_mask(ifp->options->dstmask, DHO_ROUTER) ||
-           (state = D_CSTATE(ifp)) == NULL)
-               return rt;
-
-       if ((r = calloc(1, sizeof(*r))) == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-               ipv4_freeroutes(rt);
-               return NULL;
-       }
-       r->dest.s_addr = INADDR_ANY;
-       r->mask.s_addr = INADDR_ANY;
-       r->gate = state->addr->brd;
-       r->mtu = dhcp_get_mtu(ifp);
-       r->src = state->addr->addr;
-       TAILQ_INSERT_HEAD(rt, r, next);
-       return rt;
+       return n;
 }
 
 /* We should check to ensure the routers are on the same subnet
  * OR supply a host route. If not, warn and add a host route. */
-static struct rt_head *
-add_router_host_route(struct rt_head *rt, const struct interface *ifp)
+static int
+inet_routerhostroute(struct rt_head *routes, struct interface *ifp)
 {
-       struct rt *rtp, *rtn;
+       struct rt *rt, *rth;
+       struct sockaddr_in *dest, *netmask, *gateway;
        const char *cp, *cp2, *cp3, *cplim;
        struct if_options *ifo;
        const struct dhcp_state *state;
-
-       if (rt == NULL) /* earlier malloc failed */
-               return NULL;
+       struct in_addr in;
+       int n;
 
        /* Don't add a host route for these interfaces. */
        if (ifp->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
-               return rt;
+               return 0;
 
-       TAILQ_FOREACH(rtp, rt, next) {
-               if (rtp->dest.s_addr != INADDR_ANY ||
-                   rtp->gate.s_addr == INADDR_ANY)
+       n = 0;
+       TAILQ_FOREACH(rt, routes, rt_next) {
+               if (rt->rt_dest.sa_family != AF_INET)
                        continue;
+               if (!sa_is_unspecified(&rt->rt_dest) ||
+                   sa_is_unspecified(&rt->rt_gateway))
+                       continue;
+               gateway = satosin(&rt->rt_gateway);
                /* Scan for a route to match */
-               TAILQ_FOREACH(rtn, rt, next) {
-                       if (rtn == rtp)
+               TAILQ_FOREACH(rth, routes, rt_next) {
+                       if (rth == rt)
                                break;
                        /* match host */
-                       if (rtn->dest.s_addr == rtp->gate.s_addr)
+                       if (sa_cmp(&rth->rt_dest, &rt->rt_gateway) == 0)
                                break;
                        /* match subnet */
-                       cp = (const char *)&rtp->gate.s_addr;
-                       cp2 = (const char *)&rtn->dest.s_addr;
-                       cp3 = (const char *)&rtn->mask.s_addr;
-                       cplim = cp3 + sizeof(rtn->mask.s_addr);
+                       cp = (const char *)&gateway->sin_addr.s_addr;
+                       dest = satosin(&rth->rt_dest);
+                       cp2 = (const char *)&dest->sin_addr.s_addr;
+                       netmask = satosin(&rth->rt_netmask);
+                       cp3 = (const char *)&netmask->sin_addr.s_addr;
+                       cplim = cp3 + sizeof(netmask->sin_addr.s_addr);
                        while (cp3 < cplim) {
                                if ((*cp++ ^ *cp2++) & *cp3++)
                                        break;
@@ -698,7 +370,7 @@ add_router_host_route(struct rt_head *rt, const struct interface *ifp)
                        if (cp3 == cplim)
                                break;
                }
-               if (rtn != rtp)
+               if (rth != rt)
                        continue;
                if ((state = D_CSTATE(ifp)) == NULL)
                        continue;
@@ -707,181 +379,78 @@ add_router_host_route(struct rt_head *rt, const struct interface *ifp)
                        if (!(ifo->options & DHCPCD_ROUTER_HOST_ROUTE_WARNED) &&
                            !(state->added & STATE_FAKE))
                        {
+                               char buf[INET_MAX_ADDRSTRLEN];
+
                                ifo->options |= DHCPCD_ROUTER_HOST_ROUTE_WARNED;
                                logger(ifp->ctx, LOG_WARNING,
                                    "%s: forcing router %s through interface",
-                                   ifp->name, inet_ntoa(rtp->gate));
+                                   ifp->name,
+                                   sa_addrtop(&rt->rt_gateway,
+                                   buf, sizeof(buf)));
                        }
-                       rtp->gate.s_addr = 0;
+                       gateway->sin_addr.s_addr = INADDR_ANY;
                        continue;
                }
                if (!(ifo->options & DHCPCD_ROUTER_HOST_ROUTE_WARNED) &&
                    !(state->added & STATE_FAKE))
                {
+                       char buf[INET_MAX_ADDRSTRLEN];
+
                        ifo->options |= DHCPCD_ROUTER_HOST_ROUTE_WARNED;
                        logger(ifp->ctx, LOG_WARNING,
                            "%s: router %s requires a host route",
-                           ifp->name, inet_ntoa(rtp->gate));
-               }
-               if ((rtn = calloc(1, sizeof(*rtn))) == NULL) {
-                       logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                       ipv4_freeroutes(rt);
-                       return NULL;
-               }
-               rtn->dest.s_addr = rtp->gate.s_addr;
-               rtn->mask.s_addr = htonl(INADDR_BROADCAST);
-               rtn->gate.s_addr = htonl(INADDR_ANY);
-               rtn->mtu = dhcp_get_mtu(ifp);
-               rtn->src = state->addr->addr;
-               TAILQ_INSERT_BEFORE(rtp, rtn, next);
-       }
-       return rt;
-}
-
-static int
-ipv4_doroute(struct rt *rt, struct rt_head *nrs)
-{
-       const struct dhcp_state *state;
-       struct rt *or;
-
-       state = D_CSTATE(rt->iface);
-       rt->state = state->added & STATE_FAKE;
-#ifdef HAVE_ROUTE_METRIC
-       rt->metric = rt->iface->metric;
-#endif
-       /* Is this route already in our table? */
-       if ((find_route(nrs, rt, NULL)) != NULL)
-               return 0;
-       /* Do we already manage it? */
-       if ((or = find_route(rt->iface->ctx->ipv4_routes, rt, NULL))) {
-               if (state->added & STATE_FAKE)
-                       return 0;
-               if (or->state & STATE_FAKE ||
-                   or->iface != rt->iface ||
-#ifdef HAVE_ROUTE_METRIC
-                   or->metric != rt->metric ||
-#endif
-                   or->gate.s_addr != rt->gate.s_addr ||
-                   or->src.s_addr != rt->src.s_addr ||
-                   or->mtu != rt->mtu)
-               {
-                       if (c_route(or, rt) != 0)
-                               return 0;
-               }
-               TAILQ_REMOVE(rt->iface->ctx->ipv4_routes, or, next);
-               free(or);
-       } else {
-               if (state->added & STATE_FAKE) {
-                       if ((or = ipv4_findrt(rt->iface->ctx, rt, 1)) == NULL)
-                               return 0;
-                       rt->iface = or->iface;
-                       rt->gate.s_addr = or->gate.s_addr;
-#ifdef HAVE_ROUTE_METRIC
-                       rt->metric = or->metric;
-#endif
-                       rt->mtu = or->mtu;
-                       rt->flags = or->flags;
-               } else {
-                       if (n_route(rt) != 0)
-                               return 0;
+                           ifp->name,
+                           sa_addrtop(&rt->rt_gateway, buf, sizeof(buf)));
                }
+               if ((rth = rt_new(ifp)) == NULL)
+                       return -1;
+               sa_in_init(&rth->rt_dest, &gateway->sin_addr);
+               in.s_addr = INADDR_BROADCAST;
+               sa_in_init(&rth->rt_netmask, &in);
+               in.s_addr = INADDR_ANY;
+               sa_in_init(&rth->rt_gateway, &in);
+               rth->rt_mtu = dhcp_get_mtu(ifp);
+               sa_in_init(&rth->rt_ifa, &state->addr->addr);
+               TAILQ_INSERT_BEFORE(rt, rth, rt_next);
+               n++;
        }
-       return 1;
+       return n;
 }
 
-void
-ipv4_buildroutes(struct dhcpcd_ctx *ctx)
+bool
+inet_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes)
 {
-       struct rt_head *nrs, *dnr;
-       struct rt *rt, *rtn;
        struct interface *ifp;
-       const struct dhcp_state *state;
-       int has_default;
-
-       /* We need to have the interfaces in the correct order to ensure
-        * our routes are managed correctly. */
-       if_sortinterfaces(ctx);
-
-       if ((nrs = malloc(sizeof(*nrs))) == NULL) {
-               logger(ctx, LOG_ERR, "%s: %m", __func__);
-               return;
-       }
-       TAILQ_INIT(nrs);
+       struct rt def;
+       bool have_default;
 
-       has_default = 0;
        TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-               state = D_CSTATE(ifp);
-               if (state != NULL && state->new != NULL && state->added) {
-                       dnr = get_routes(ifp);
-                       dnr = add_subnet_route(dnr, ifp);
-               } else
-                       dnr = NULL;
-               if ((rt = ipv4ll_subnet_route(ifp)) != NULL) {
-                       if (dnr == NULL) {
-                               if ((dnr = malloc(sizeof(*dnr))) == NULL) {
-                                       logger(ifp->ctx, LOG_ERR,
-                                           "%s: malloc %m", __func__);
-                                       free(rt);
-                                       continue;
-                               }
-                               TAILQ_INIT(dnr);
-                       }
-                       TAILQ_INSERT_HEAD(dnr, rt, next);
-               }
-               if (dnr == NULL)
+               if (!ifp->active)
                        continue;
-#ifdef IPV4_LOOPBACK_ROUTE
-               dnr = add_loopback_route(dnr, ifp);
-#endif
+               if (inet_dhcproutes(routes, ifp) == -1)
+                       return false;
+               if (ipv4ll_subnetroute(routes, ifp) == -1)
+                       return false;
                if (ifp->options->options & DHCPCD_GATEWAY) {
-                       dnr = add_router_host_route(dnr, ifp);
-                       dnr = add_destination_route(dnr, ifp);
+                       if (inet_routerhostroute(routes, ifp) == -1)
+                               return false;
                }
-               if (dnr == NULL)
-                       continue;
-               TAILQ_FOREACH_SAFE(rt, dnr, next, rtn) {
-                       rt->iface = ifp;
-                       if (ipv4_doroute(rt, nrs) == 1) {
-                               TAILQ_REMOVE(dnr, rt, next);
-                               TAILQ_INSERT_TAIL(nrs, rt, next);
-                               if (rt->dest.s_addr == INADDR_ANY)
-                                       has_default = 1;
-                       }
-               }
-               ipv4_freeroutes(dnr);
        }
 
-       /* If there is no default route, grab one without a
-        * gateway for any IPv4LL enabled interfaces. */
-       if (!has_default) {
-               struct rt def;
-
-               memset(&def, 0, sizeof(def));
-               if (ipv4_findrt(ctx, &def, 0) == NULL) {
-                       TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-                               if ((rt = ipv4ll_default_route(ifp)) != NULL) {
-                                       if (ipv4_doroute(rt, nrs) == 1) {
-                                               TAILQ_INSERT_TAIL(nrs, rt, next);
-                                               break;
-                                       } else
-                                               free(rt);
-                               }
-                       }
+       /* 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;
+       have_default = (rt_find(routes, &def) != NULL);
+       if (!have_default) {
+               TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+                       if (ifp->active &&
+                           ifp->options->options & DHCPCD_GATEWAY &&
+                           ipv4ll_defaultroute(routes, ifp) == 1)
+                               break;
                }
        }
 
-       /* Remove old routes we used to manage */
-       if (ctx->ipv4_routes) {
-               TAILQ_FOREACH_REVERSE(rt, ctx->ipv4_routes, rt_head, next) {
-                       if (find_route(nrs, rt, NULL) == NULL &&
-                           (rt->iface->options->options &
-                           (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
-                           (DHCPCD_EXITING | DHCPCD_PERSISTENT))
-                               d_route(rt);
-               }
-       }
-       ipv4_freeroutes(ctx->ipv4_routes);
-       ctx->ipv4_routes = nrs;
+       return true;
 }
 
 int
@@ -964,7 +533,6 @@ ipv4_getstate(struct interface *ifp)
                        return NULL;
                }
                TAILQ_INIT(&state->addrs);
-               TAILQ_INIT(&state->routes);
 #ifdef BSD
                state->buffer_size = state->buffer_len = state->buffer_pos = 0;
                state->buffer = NULL;
@@ -1142,7 +710,7 @@ ipv4_preferanother(struct interface *ifp)
        }
 
 out:
-       ipv4_buildroutes(ifp->ctx);
+       rt_build(ifp->ctx, AF_INET);
        return preferred;
 }
 
@@ -1168,11 +736,11 @@ ipv4_applyaddr(void *arg)
                {
                        if (state->added && !ipv4_preferanother(ifp)) {
                                delete_address(ifp);
-                               ipv4_buildroutes(ifp->ctx);
+                               rt_build(ifp->ctx, AF_INET);
                        }
                        script_runreason(ifp, state->reason);
                } else
-                       ipv4_buildroutes(ifp->ctx);
+                       rt_build(ifp->ctx, AF_INET);
                return;
        }
 
@@ -1261,7 +829,7 @@ ipv4_applyaddr(void *arg)
         * We do this because we cannot rely on recieving the kernel
         * notification right now via our link socket. */
        if_initrt(ifp->ctx);
-       ipv4_buildroutes(ifp->ctx);
+       rt_build(ifp->ctx, AF_INET);
 
 #ifdef ARP
        /* Announce the address */
@@ -1369,7 +937,6 @@ ipv4_free(struct interface *ifp)
                                TAILQ_REMOVE(&state->addrs, ia, next);
                                free(ia);
                        }
-                       ipv4_freerts(&state->routes);
 #ifdef BSD
                        free(state->buffer);
 #endif
@@ -1377,11 +944,3 @@ ipv4_free(struct interface *ifp)
                }
        }
 }
-
-void
-ipv4_ctxfree(struct dhcpcd_ctx *ctx)
-{
-
-       ipv4_freeroutes(ctx->ipv4_routes);
-       ipv4_freeroutes(ctx->ipv4_kroutes);
-}
diff --git a/ipv4.h b/ipv4.h
index c24a38eff02d8fa69e289b6733719af7f4bf47ab..335445f74cd3fe9ba5172e10078747755d336628 100644 (file)
--- a/ipv4.h
+++ b/ipv4.h
         (IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED)
 #endif
 
-struct rt {
-       TAILQ_ENTRY(rt) next;
-       struct in_addr dest;
-       struct in_addr mask;
-       struct in_addr gate;
-       const struct interface *iface;
-#ifdef HAVE_ROUTE_METRIC
-       unsigned int metric;
-#endif
-       unsigned int mtu;
-       struct in_addr src;
-       unsigned int flags;
-       unsigned int state;
-};
-TAILQ_HEAD(rt_head, rt);
-
 struct ipv4_addr {
        TAILQ_ENTRY(ipv4_addr) next;
        struct in_addr addr;
@@ -110,7 +94,6 @@ TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
 
 struct ipv4_state {
        struct ipv4_addrhead addrs;
-       struct rt_head routes;
 
 #ifdef BSD
        /* Buffer for BPF */
@@ -126,24 +109,22 @@ struct ipv4_state {
 
 #ifdef INET
 struct ipv4_state *ipv4_getstate(struct interface *);
-int ipv4_init(struct dhcpcd_ctx *);
 int ipv4_ifcmp(const struct interface *, const struct interface *);
 uint8_t inet_ntocidr(struct in_addr);
 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 *, struct rt_head *);
+
 #define STATE_ADDED            0x01
 #define STATE_FAKE             0x02
 
-void ipv4_buildroutes(struct dhcpcd_ctx *);
 int ipv4_deladdr(struct ipv4_addr *, int);
 int ipv4_preferanother(struct interface *);
 struct ipv4_addr *ipv4_addaddr(struct interface *,
     const struct in_addr *, const struct in_addr *, const struct in_addr *);
 void ipv4_applyaddr(void *);
-int ipv4_handlert(struct dhcpcd_ctx *, int, const struct rt *, int);
-void ipv4_freerts(struct rt_head *);
 
 struct ipv4_addr *ipv4_iffindaddr(struct interface *,
     const struct in_addr *, const struct in_addr *);
@@ -155,17 +136,11 @@ void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,
     const struct in_addr *, const struct in_addr *, const struct in_addr *,
     int);
 
-void ipv4_freeroutes(struct rt_head *);
-
 void ipv4_free(struct interface *);
-void ipv4_ctxfree(struct dhcpcd_ctx *);
 #else
-#define ipv4_init(a) (-1)
 #define ipv4_sortinterfaces(a) {}
 #define ipv4_applyaddr(a) {}
-#define ipv4_freeroutes(a) {}
 #define ipv4_free(a) {}
-#define ipv4_ctxfree(a) {}
 #define ipv4_hasaddr(a) (0)
 #define ipv4_preferanother(a) {}
 #endif
index 77e678aa5f8466ba1af254267a534c3c22feda7b..1b6ce4e760d2ecb38df8c8cb8070a58b1a98d26a 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -30,6 +30,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -43,6 +44,7 @@
 #include "if-options.h"
 #include "ipv4.h"
 #include "ipv4ll.h"
+#include "sa.h"
 #include "script.h"
 
 #ifdef IPV4LL
@@ -84,50 +86,54 @@ ipv4ll_pickaddr(struct arp_state *astate)
        return addr.s_addr;
 }
 
-struct rt *
-ipv4ll_subnet_route(const struct interface *ifp)
+int
+ipv4ll_subnetroute(struct rt_head *routes, struct interface *ifp)
 {
-       const struct ipv4ll_state *state;
+       struct ipv4ll_state *state;
        struct rt *rt;
+       struct in_addr in;
 
        assert(ifp != NULL);
-       if ((state = IPV4LL_CSTATE(ifp)) == NULL ||
+       if ((state = IPV4LL_STATE(ifp)) == NULL ||
            state->addr == NULL)
-               return NULL;
+               return 0;
 
-       if ((rt = calloc(1, sizeof(*rt))) == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: malloc: %m", __func__);
-               return NULL;
-       }
-       rt->iface = ifp;
-       rt->dest.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
-       rt->mask.s_addr = state->addr->mask.s_addr;
-       rt->gate.s_addr = INADDR_ANY;
-       rt->src = state->addr->addr;
-       return rt;
+       if ((rt = rt_new(ifp)) == NULL)
+               return -1;
+
+       in.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
+       sa_in_init(&rt->rt_dest, &in);
+       in.s_addr = state->addr->mask.s_addr;
+       sa_in_init(&rt->rt_netmask, &in);
+       in.s_addr = INADDR_ANY;
+       sa_in_init(&rt->rt_gateway, &in);
+       sa_in_init(&rt->rt_ifa, &state->addr->addr);
+       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+       return 1;
 }
 
-struct rt *
-ipv4ll_default_route(const struct interface *ifp)
+int
+ipv4ll_defaultroute(struct rt_head *routes, struct interface *ifp)
 {
-       const struct ipv4ll_state *state;
+       struct ipv4ll_state *state;
        struct rt *rt;
+       struct in_addr in;
 
        assert(ifp != NULL);
-       if ((state = IPV4LL_CSTATE(ifp)) == NULL ||
+       if ((state = IPV4LL_STATE(ifp)) == NULL ||
            state->addr == NULL)
-               return NULL;
+               return 0;
 
-       if ((rt = calloc(1, sizeof(*rt))) == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: malloc: %m", __func__);
-               return NULL;
-       }
-       rt->iface = ifp;
-       rt->dest.s_addr = INADDR_ANY;
-       rt->mask.s_addr = INADDR_ANY;
-       rt->gate.s_addr = INADDR_ANY;
-       rt->src = state->addr->addr;
-       return rt;
+       if ((rt = rt_new(ifp)) == NULL)
+               return -1;
+
+       in.s_addr = INADDR_ANY;
+       sa_in_init(&rt->rt_dest, &in);
+       sa_in_init(&rt->rt_netmask, &in);
+       sa_in_init(&rt->rt_gateway, &in);
+       sa_in_init(&rt->rt_ifa, &state->addr->addr);
+       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+       return 1;
 }
 
 ssize_t
@@ -206,8 +212,7 @@ test:
                return;
        }
        timespecclear(&state->defend);
-       if_initrt(ifp->ctx);
-       ipv4_buildroutes(ifp->ctx);
+       rt_build(ifp->ctx, AF_INET);
        arp_announce(astate);
        script_runreason(ifp, "IPV4LL");
        dhcpcd_daemonise(ifp->ctx);
@@ -466,7 +471,7 @@ ipv4ll_freedrop(struct interface *ifp, int drop)
                ifp->if_data[IF_DATA_IPV4LL] = NULL;
 
                if (dropped) {
-                       ipv4_buildroutes(ifp->ctx);
+                       rt_build(ifp->ctx, AF_INET);
                        script_runreason(ifp, "IPV4LL");
                }
        }
@@ -476,19 +481,25 @@ ipv4ll_freedrop(struct interface *ifp, int drop)
  * daemon would solve this issue easily. */
 #ifdef HAVE_ROUTE_METRIC
 int
-ipv4ll_handlert(struct dhcpcd_ctx *ctx, __unused int cmd, const struct rt *rt)
+ipv4ll_recvrt(__unused int cmd, const struct rt *rt)
 {
+       struct dhcpcd_ctx *ctx;
        struct interface *ifp;
 
+       /* Ignore route init. */
+       if (rt->rt_dflags & RTDF_INIT)
+               return 0;
+
        /* Only interested in default route changes. */
-       if (rt->dest.s_addr != INADDR_ANY)
+       if (sa_is_unspecified(&rt->rt_dest))
                return 0;
 
        /* If any interface is running IPv4LL, rebuild our routing table. */
+       ctx = rt->rt_ifp->ctx;
        TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                if (IPV4LL_STATE_RUNNING(ifp)) {
-                       if_initrt(ifp->ctx);
-                       ipv4_buildroutes(ifp->ctx);
+                       if_initrt(ctx);
+                       rt_build(ctx, AF_INET);
                        break;
                }
        }
index 34a185ff6f97627c63a82084fac5ec785f4da6c2..6ab659ce0ffde0124bebd4279be00a557cc2fb65 100644 (file)
--- a/ipv4ll.h
+++ b/ipv4ll.h
@@ -56,16 +56,14 @@ struct ipv4ll_state {
        (IPV4LL_CSTATE((ifp)) && !IPV4LL_CSTATE((ifp))->down &&                \
        (IPV4LL_CSTATE((ifp))->addr != NULL))
 
-struct rt* ipv4ll_subnet_route(const struct interface *);
-struct rt* ipv4ll_default_route(const struct interface *);
+int ipv4ll_subnetroute(struct rt_head *, struct interface *);
+int ipv4ll_defaultroute(struct rt_head *,struct interface *);
 ssize_t ipv4ll_env(char **, const char *, const struct interface *);
 void ipv4ll_start(void *);
 void ipv4ll_claimed(void *);
 void ipv4ll_handle_failure(void *);
 #ifdef HAVE_ROUTE_METRIC
-int ipv4ll_handlert(struct dhcpcd_ctx *, int, const struct rt *);
-#else
-#define        ipv4ll_handlert(a, b, c)        (0)
+int ipv4ll_recvrt(int, const struct rt *);
 #endif
 
 #define        ipv4ll_free(ifp) ipv4ll_freedrop((ifp), 0);
diff --git a/ipv6.c b/ipv6.c
index ff48c44ab1dd9c3c703e0209f5011b591513d6b4..4514bb98f9add3fbf0308f7c1ea25c5d8b75823b 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -65,6 +65,7 @@
 #include "eloop.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
+#include "sa.h"
 #include "script.h"
 
 #ifdef HAVE_MD5_H
@@ -138,23 +139,13 @@ ipv6_init(struct dhcpcd_ctx *dhcpcd_ctx)
        if (ctx == NULL)
                return NULL;
 
-       ctx->routes = malloc(sizeof(*ctx->routes));
-       if (ctx->routes == NULL) {
-               free(ctx);
-               return NULL;
-       }
-       TAILQ_INIT(ctx->routes);
-
        ctx->ra_routers = malloc(sizeof(*ctx->ra_routers));
        if (ctx->ra_routers == NULL) {
-               free(ctx->routes);
                free(ctx);
                return NULL;
        }
        TAILQ_INIT(ctx->ra_routers);
 
-       TAILQ_INIT(&ctx->kroutes);
-
        ctx->sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
        ctx->sndhdr.msg_iov = ctx->sndiov;
        ctx->sndhdr.msg_iovlen = 1;
@@ -1598,8 +1589,8 @@ ipv6_startstatic(struct interface *ifp)
        ia->prefix_pltime = ND6_INFINITE_LIFETIME;
        ia->dadcallback = ipv6_staticdadcallback;
        ipv6_addaddr(ia, NULL);
-       if_initrt6(ifp->ctx);
-       ipv6_buildroutes(ifp->ctx);
+       if_initrt(ifp->ctx);
+       rt_build(ifp->ctx, AF_INET6);
        if (run_script)
                script_runreason(ifp, "STATIC6");
        return 1;
@@ -1642,7 +1633,7 @@ ipv6_start(struct interface *ifp)
        }
 
        /* Load existing routes */
-       if_initrt6(ifp->ctx);
+       if_initrt(ifp->ctx);
        return 0;
 }
 
@@ -1667,8 +1658,8 @@ ipv6_freedrop(struct interface *ifp, int drop)
        ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL);
        if (drop) {
                if (ifp->ctx->ipv6 != NULL) {
-                       if_initrt6(ifp->ctx);
-                       ipv6_buildroutes(ifp->ctx);
+                       if_initrt(ifp->ctx);
+                       rt_build(ifp->ctx, AF_INET6);
                }
        } else {
                /* Because we need to cache the addresses we don't control,
@@ -1687,10 +1678,7 @@ ipv6_ctxfree(struct dhcpcd_ctx *ctx)
                return;
 
        free(ctx->secret);
-       ipv6_freerts(ctx->ipv6->routes);
-       free(ctx->ipv6->routes);
        free(ctx->ipv6->ra_routers);
-       ipv6_freerts(&ctx->ipv6->kroutes);
        free(ctx->ipv6);
 }
 
@@ -2109,247 +2097,29 @@ ipv6_regentempifid(void *arg)
 }
 #endif /* IPV6_MANAGETEMPADDR */
 
-static struct rt6 *
-find_route6(struct rt6_head *rts, const struct rt6 *r)
-{
-       struct rt6 *rt;
-
-       TAILQ_FOREACH(rt, rts, next) {
-               if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) &&
-#ifdef HAVE_ROUTE_METRIC
-                   (r->iface == NULL || rt->iface == NULL ||
-                   rt->iface->metric == r->iface->metric) &&
-#endif
-                   IN6_ARE_ADDR_EQUAL(&rt->mask, &r->mask))
-                       return rt;
-       }
-       return NULL;
-}
-
-static void
-desc_route(const char *cmd, const struct rt6 *rt)
-{
-       char destbuf[INET6_ADDRSTRLEN];
-       char gatebuf[INET6_ADDRSTRLEN];
-       const char *ifname, *dest, *gate;
-       struct dhcpcd_ctx *ctx;
-
-       ctx = rt->iface ? rt->iface->ctx : NULL;
-       ifname = rt->iface ? rt->iface->name : "(no iface)";
-       dest = inet_ntop(AF_INET6, &rt->dest, destbuf, INET6_ADDRSTRLEN);
-       gate = inet_ntop(AF_INET6, &rt->gate, gatebuf, INET6_ADDRSTRLEN);
-       if (IN6_ARE_ADDR_EQUAL(&rt->gate, &in6addr_any))
-               logger(ctx, LOG_INFO, "%s: %s route to %s/%d",
-                   ifname, cmd, dest, ipv6_prefixlen(&rt->mask));
-       else if (IN6_ARE_ADDR_EQUAL(&rt->dest, &in6addr_any) &&
-           IN6_ARE_ADDR_EQUAL(&rt->mask, &in6addr_any))
-               logger(ctx, LOG_INFO, "%s: %s default route via %s",
-                   ifname, cmd, gate);
-       else
-               logger(ctx, LOG_INFO, "%s: %s%s route to %s/%d via %s",
-                   ifname, cmd,
-                   rt->flags & RTF_REJECT ? " reject" : "",
-                   dest, ipv6_prefixlen(&rt->mask), gate);
-}
-
-static struct rt6*
-ipv6_findrt(struct dhcpcd_ctx *ctx, const struct rt6 *rt, int flags)
-{
-       struct rt6 *r;
-
-       TAILQ_FOREACH(r, &ctx->ipv6->kroutes, next) {
-               if (IN6_ARE_ADDR_EQUAL(&rt->dest, &r->dest) &&
-#ifdef HAVE_ROUTE_METRIC
-                   (rt->iface == r->iface ||
-                   (rt->flags & RTF_REJECT && r->flags & RTF_REJECT)) &&
-                   (!flags || rt->metric == r->metric) &&
-#else
-                   (!flags || rt->iface == r->iface ||
-                   (rt->flags & RTF_REJECT && r->flags & RTF_REJECT)) &&
-#endif
-                   IN6_ARE_ADDR_EQUAL(&rt->mask, &r->mask))
-                       return r;
-       }
-       return NULL;
-}
-
-void
-ipv6_freerts(struct rt6_head *routes)
-{
-       struct rt6 *rt;
-
-       while ((rt = TAILQ_FIRST(routes))) {
-               TAILQ_REMOVE(routes, rt, next);
-               free(rt);
-       }
-}
 
-/* If something other than dhcpcd removes a route,
- * we need to remove it from our internal table. */
-int
-ipv6_handlert(struct dhcpcd_ctx *ctx, int cmd, const struct rt6 *rt)
+static struct rt *
+inet6_makeroute(struct interface *ifp, const struct ra *rap)
 {
-       struct rt6 *f;
-
-       if (ctx->ipv6 == NULL)
-               return 0;
+       struct rt *rt;
 
-       f = ipv6_findrt(ctx, rt, 1);
-       switch(cmd) {
-       case RTM_ADD:
-               if (f == NULL) {
-                       if ((f = malloc(sizeof(*f))) == NULL)
-                               return -1;
-                       *f = *rt;
-                       TAILQ_INSERT_TAIL(&ctx->ipv6->kroutes, f, next);
-               }
-               break;
-       case RTM_DELETE:
-               if (f) {
-                       TAILQ_REMOVE(&ctx->ipv6->kroutes, f, next);
-                       free(f);
-               }
-               /* If we manage the route, remove it */
-               if ((f = find_route6(ctx->ipv6->routes, rt))) {
-                       desc_route("deleted", f);
-                       TAILQ_REMOVE(ctx->ipv6->routes, f, next);
-                       free(f);
-               }
-               break;
-       }
-       return 0;
-}
-
-#define n_route(a)      nc_route(NULL, a)
-#define c_route(a, b)   nc_route(a, b)
-static int
-nc_route(struct rt6 *ort, struct rt6 *nrt)
-{
-       int change;
-
-       /* Don't set default routes if not asked to */
-       if (IN6_IS_ADDR_UNSPECIFIED(&nrt->dest) &&
-           IN6_IS_ADDR_UNSPECIFIED(&nrt->mask) &&
-           !(nrt->iface->options->options & DHCPCD_GATEWAY))
-               return -1;
-
-       desc_route(ort == NULL ? "adding" : "changing", nrt);
-
-       change = 0;
-       if (ort == NULL) {
-               ort = ipv6_findrt(nrt->iface->ctx, nrt, 0);
-               if (ort &&
-                   ((ort->flags & RTF_REJECT && nrt->flags & RTF_REJECT) ||
-                    (ort->iface == nrt->iface &&
-#ifdef HAVE_ROUTE_METRIC
-                   ort->metric == nrt->metric &&
-#endif
-                   IN6_ARE_ADDR_EQUAL(&ort->gate, &nrt->gate))))
-               {
-                       if (ort->mtu == nrt->mtu)
-                               return 0;
-                       change = 1;
-               }
-       }
-
-#ifdef RTF_CLONING
-       /* BSD can set routes to be cloning routes.
-        * Cloned routes inherit the parent flags.
-        * As such, we need to delete and re-add the route to flush children
-        * to correct the flags. */
-       if (change && ort != NULL && ort->flags & RTF_CLONING)
-               change = 0;
-#endif
-
-       if (change) {
-               if (if_route6(RTM_CHANGE, nrt) != -1)
-                       return 0;
-               if (errno != ESRCH)
-                       logger(nrt->iface->ctx, LOG_ERR, "if_route6 (CHG): %m");
-       }
-
-#ifdef HAVE_ROUTE_METRIC
-       /* With route metrics, we can safely add the new route before
-        * deleting the old route. */
-       if (if_route6(RTM_ADD, nrt) != -1) {
-               if (ort && if_route6(RTM_DELETE, ort) == -1 &&
-                   errno != ESRCH)
-                       logger(nrt->iface->ctx, LOG_ERR, "if_route6 (DEL): %m");
-               return 0;
-       }
-
-       /* If the kernel claims the route exists we need to rip out the
-        * old one first. */
-       if (errno != EEXIST || ort == NULL)
-               goto logerr;
-#endif
-
-       /* No route metrics, we need to delete the old route before
-        * adding the new one. */
-#ifdef ROUTE_PER_GATEWAY
-       errno = 0;
-#endif
-       if (ort && if_route6(RTM_DELETE, ort) == -1 && errno != ESRCH)
-               logger(nrt->iface->ctx, LOG_ERR, "if_route6 (DEL): %m");
-#ifdef ROUTE_PER_GATEWAY
-       /* The OS allows many routes to the same dest with different gateways.
-        * dhcpcd does not support this yet, so for the time being just keep on
-        * deleting the route until there is an error. */
-       if (ort && errno == 0) {
-               for (;;) {
-                       if (if_route6(RTM_DELETE, ort) == -1)
-                               break;
-               }
-       }
-#endif
-       if (if_route6(RTM_ADD, nrt) != -1)
-               return 0;
-#ifdef HAVE_ROUTE_METRIC
-logerr:
-#endif
-       logger(nrt->iface->ctx, LOG_ERR, "if_route6 (ADD): %m");
-       return -1;
-}
-
-static int
-d_route(struct rt6 *rt)
-{
-       int retval;
-
-       desc_route("deleting", rt);
-       retval = if_route6(RTM_DELETE, rt) == -1 ? -1 : 0;
-       if (retval == -1 && errno != ENOENT && errno != ESRCH)
-               logger(rt->iface->ctx, LOG_ERR,
-                   "%s: if_delroute6: %m", rt->iface->name);
-       return retval;
-}
-
-static struct rt6 *
-make_route(const struct interface *ifp, const struct ra *rap)
-{
-       struct rt6 *r;
-
-       r = calloc(1, sizeof(*r));
-       if (r == NULL) {
-               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
+       if ((rt = rt_new(ifp)) == NULL)
                return NULL;
-       }
-       r->iface = ifp;
+
 #ifdef HAVE_ROUTE_METRIC
-       r->metric = ifp->metric;
+       rt->rt_metric = ifp->metric;
 #endif
-       if (rap)
-               r->mtu = rap->mtu;
-       else
-               r->mtu = 0;
-       return r;
+       if (rap != NULL)
+               rt->rt_mtu = rap->mtu;
+       return rt;
 }
 
-static struct rt6 *
-make_prefix(const struct interface *ifp, const struct ra *rap,
+static struct rt *
+inet6_makeprefix(struct interface *ifp, const struct ra *rap,
     const struct ipv6_addr *addr)
 {
-       struct rt6 *r;
+       struct rt *rt;
+       struct in6_addr netmask;
 
        if (addr == NULL || addr->prefix_len > 128) {
                errno = EINVAL;
@@ -2383,224 +2153,168 @@ make_prefix(const struct interface *ifp, const struct ra *rap,
                        ifp = lo0;
        }
 
-       r = make_route(ifp, rap);
-       if (r == NULL)
+       if ((rt = inet6_makeroute(ifp, rap)) == NULL)
                return NULL;
-       r->dest = addr->prefix;
-       ipv6_mask(&r->mask, addr->prefix_len);
+
+       sa_in6_init(&rt->rt_dest, &addr->prefix);
+       ipv6_mask(&netmask, addr->prefix_len);
+       sa_in6_init(&rt->rt_netmask, &netmask);
        if (addr->flags & IPV6_AF_DELEGATEDPFX) {
-               r->flags |= RTF_REJECT;
-               r->gate = in6addr_loopback;
+               rt->rt_flags |= RTF_REJECT;
+               sa_in6_init(&rt->rt_gateway, &in6addr_loopback);
        } else
-               r->gate = in6addr_any;
-       r->src = addr->addr;
-       return r;
+               rt->rt_gateway.sa_family = AF_UNSPEC;
+       sa_in6_init(&rt->rt_ifa, &addr->addr);
+       return rt;
 }
 
-static struct rt6 *
-make_router(const struct ra *rap)
+static struct rt *
+inet6_makerouter(struct ra *rap)
 {
-       struct rt6 *r;
+       struct rt *rt;
 
-       r = make_route(rap->iface, rap);
-       if (r == NULL)
+       if ((rt = inet6_makeroute(rap->iface, rap)) == NULL)
                return NULL;
-       r->dest = in6addr_any;
-       r->mask = in6addr_any;
-       r->gate = rap->from;
-       return r;
+       sa_in6_init(&rt->rt_dest, &in6addr_any);
+       sa_in6_init(&rt->rt_netmask, &in6addr_any);
+       sa_in6_init(&rt->rt_gateway, &rap->from);
+       return rt;
 }
 
 #define RT_IS_DEFAULT(rtp) \
        (IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) &&                  \
            IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any))
 
-static void
-ipv6_build_static_routes(struct dhcpcd_ctx *ctx, struct rt6_head *dnr)
+static int
+inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx)
 {
-       const struct interface *ifp;
-       const struct ipv6_state *state;
-       const struct ipv6_addr *ia;
-       struct rt6 *rt;
+       struct interface *ifp;
+       struct ipv6_state *state;
+       struct ipv6_addr *ia;
+       struct rt *rt;
+       int n;
 
+       n = 0;
        TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-               if ((state = IPV6_CSTATE(ifp)) == NULL)
+               if ((state = IPV6_STATE(ifp)) == NULL)
                        continue;
                TAILQ_FOREACH(ia, &state->addrs, next) {
                        if ((ia->flags & (IPV6_AF_ADDED | IPV6_AF_STATIC)) ==
                            (IPV6_AF_ADDED | IPV6_AF_STATIC))
                        {
-                               rt = make_prefix(ifp, NULL, ia);
-                               if (rt)
-                                       TAILQ_INSERT_TAIL(dnr, rt, next);
+                               rt = inet6_makeprefix(ifp, NULL, ia);
+                               if (rt) {
+                                       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+                                       n++;
+                               }
                        }
                }
        }
+       return n;
 }
 
-static void
-ipv6_build_ra_routes(struct ipv6_ctx *ctx, struct rt6_head *dnr, int expired)
+static int
+inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired,
+    bool *have_default)
 {
-       struct rt6 *rt;
+       struct rt *rt;
        struct ra *rap;
        const struct ipv6_addr *addr;
+       int n;
 
-       TAILQ_FOREACH(rap, ctx->ra_routers, next) {
+       n = 0;
+       TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) {
                if (rap->expired != expired)
                        continue;
                if (rap->iface->options->options & DHCPCD_IPV6RA_OWN) {
                        TAILQ_FOREACH(addr, &rap->addrs, next) {
                                if (addr->prefix_vltime == 0)
                                        continue;
-                               rt = make_prefix(rap->iface, rap, addr);
-                               if (rt)
-                                       TAILQ_INSERT_TAIL(dnr, rt, next);
+                               rt = inet6_makeprefix(rap->iface, rap, addr);
+                               if (rt) {
+                                       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+                                       n++;
+                               }
                        }
                }
                if (rap->lifetime && rap->iface->options->options &
                    (DHCPCD_IPV6RA_OWN | DHCPCD_IPV6RA_OWN_DEFAULT))
                {
-                       rt = make_router(rap);
-                       if (rt)
-                               TAILQ_INSERT_TAIL(dnr, rt, next);
+                       rt = inet6_makerouter(rap);
+                       if (rt) {
+                               TAILQ_INSERT_TAIL(routes, rt, rt_next);
+                               n++;
+                               if (have_default)
+                                       *have_default = true;
+                       }
                }
        }
+       return n;
 }
 
-static void
-ipv6_build_dhcp_routes(struct dhcpcd_ctx *ctx,
-    struct rt6_head *dnr, enum DH6S dstate)
+static int
+inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx,
+    enum DH6S dstate)
 {
-       const struct interface *ifp;
+       struct interface *ifp;
        const struct dhcp6_state *d6_state;
        const struct ipv6_addr *addr;
-       struct rt6 *rt;
+       struct rt *rt;
+       int n;
 
+       n = 0;
        TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                d6_state = D6_CSTATE(ifp);
                if (d6_state && d6_state->state == dstate) {
                        TAILQ_FOREACH(addr, &d6_state->addrs, next) {
-                               rt = make_prefix(ifp, NULL, addr);
-                               if (rt)
-                                       TAILQ_INSERT_TAIL(dnr, rt, next);
+                               rt = inet6_makeprefix(ifp, NULL, addr);
+                               if (rt) {
+                                       TAILQ_INSERT_TAIL(routes, rt, rt_next);
+                                       n++;
+                               }
                        }
                }
        }
+       return n;
 }
 
-void
-ipv6_buildroutes(struct dhcpcd_ctx *ctx)
+bool
+inet6_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes)
 {
-       struct rt6_head dnr, *nrs;
-       struct rt6 *rt, *rtn, *or;
-       uint8_t have_default;
-       unsigned long long o;
-
-       /* We need to have the interfaces in the correct order to ensure
-        * our routes are managed correctly. */
-       if_sortinterfaces(ctx);
-
-       TAILQ_INIT(&dnr);
+       bool have_default;
 
        /* Should static take priority? */
-       ipv6_build_static_routes(ctx, &dnr);
-#ifdef HAVE_ROUTE_METRIC
-       rt = TAILQ_LAST(&dnr, rt6_head);
-#endif
+       if (inet6_staticroutes(routes, ctx) == -1)
+               return false;
 
        /* First add reachable routers and their prefixes */
-       ipv6_build_ra_routes(ctx->ipv6, &dnr, 0);
-#ifdef HAVE_ROUTE_METRIC
-       have_default = (rt != TAILQ_LAST(&dnr, rt6_head));
-#endif
+       have_default = false;
+       if (inet6_raroutes(routes, ctx, 0, &have_default) == -1)
+               return false;
 
        /* 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 */
-       ipv6_build_dhcp_routes(ctx, &dnr, DH6S_BOUND);
-       ipv6_build_dhcp_routes(ctx, &dnr, DH6S_DELEGATED);
+       if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)
+               return false;
+       if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)
+               return false;
 
 #ifdef HAVE_ROUTE_METRIC
        /* If we have an unreachable router, we really do need to remove the
         * route to it beause it could be a lower metric than a reachable
         * router. Of course, we should at least have some routers if all
         * are unreachable. */
-       if (!have_default)
+       if (!have_default) {
 #endif
        /* Add our non-reachable routers and prefixes
         * Unsure if this is needed, but it's a close match to kernel
         * behaviour */
-       ipv6_build_ra_routes(ctx->ipv6, &dnr, 1);
-
-       nrs = malloc(sizeof(*nrs));
-       if (nrs == NULL) {
-               logger(ctx, LOG_ERR, "%s: %m", __func__);
-               return;
-       }
-       TAILQ_INIT(nrs);
-       have_default = 0;
-
-       TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) {
-               /* Is this route already in our table? */
-               if (find_route6(nrs, rt) != NULL)
-                       continue;
-               /* Do we already manage it? */
-               if ((or = find_route6(ctx->ipv6->routes, rt))) {
-                       if (or->iface != rt->iface ||
+               if (inet6_raroutes(routes, ctx, 1, NULL) == -1)
+                       return false;
 #ifdef HAVE_ROUTE_METRIC
-                           rt->metric != or->metric ||
-#endif
-                           !IN6_ARE_ADDR_EQUAL(&or->gate, &rt->gate) ||
-                           // !IN6_ARE_ADDR_EQUAL(&or->src, &rt->src) ||
-                           or->mtu != rt->mtu)
-                       {
-                               if (c_route(or, rt) != 0)
-                                       continue;
-                       }
-                       TAILQ_REMOVE(ctx->ipv6->routes, or, next);
-                       free(or);
-               } else {
-                       if (n_route(rt) != 0)
-                               continue;
-               }
-               if (RT_IS_DEFAULT(rt))
-                       have_default = 1;
-               TAILQ_REMOVE(&dnr, rt, next);
-               TAILQ_INSERT_TAIL(nrs, rt, next);
-       }
-
-       /* Free any routes we failed to add/change */
-       /* coverity[use_after_free] */
-       while ((rt = TAILQ_FIRST(&dnr)) != NULL) {
-               TAILQ_REMOVE(&dnr, rt, next);
-               free(rt);
-       }
-
-       /* Remove old routes we used to manage
-        * If we own the default route, but not RA management itself
-        * then we need to preserve the last best default route we had */
-       while ((rt = TAILQ_LAST(ctx->ipv6->routes, rt6_head)) != NULL) {
-               TAILQ_REMOVE(ctx->ipv6->routes, rt, next);
-               if (find_route6(nrs, rt) == NULL) {
-                       o = rt->iface->options ?
-                           rt->iface->options->options :
-                           rt->iface->ctx->options;
-                       if (!have_default &&
-                           (o & DHCPCD_IPV6RA_OWN_DEFAULT) &&
-                           !(o & DHCPCD_IPV6RA_OWN) &&
-                           RT_IS_DEFAULT(rt))
-                               have_default = 1;
-                               /* no need to add it back to our routing table
-                                * as we delete an exiting route when we add
-                                * a new one */
-                       else if ((o &
-                               (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
-                               (DHCPCD_EXITING | DHCPCD_PERSISTENT))
-                               d_route(rt);
-               }
-               free(rt);
        }
+#endif
 
-       free(ctx->ipv6->routes);
-       ctx->ipv6->routes = nrs;
+       return true;
 }
diff --git a/ipv6.h b/ipv6.h
index 8182d34a9898b9d72ac72762a284ced92a604ab3..602258f7a24ef323f70fd270129b906ca56f2bcf 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -201,21 +201,6 @@ struct ipv6_addr {
 #define        IPV6_AF_TEMPORARY       0X2000
 #endif
 
-struct rt6 {
-       TAILQ_ENTRY(rt6) next;
-       struct in6_addr dest;
-       struct in6_addr mask;
-       struct in6_addr gate;
-       const struct interface *iface;
-       struct in6_addr src;
-       unsigned int flags;
-#ifdef HAVE_ROUTE_METRIC
-       unsigned int metric;
-#endif
-       unsigned int mtu;
-};
-TAILQ_HEAD(rt6_head, rt6);
-
 struct ll_callback {
        TAILQ_ENTRY(ll_callback) next;
        void (*callback)(void *);
@@ -273,9 +258,6 @@ struct ipv6_ctx {
 
        int nd_fd;
        struct ra_head *ra_routers;
-       struct rt6_head *routes;
-
-       struct rt6_head kroutes;
 
        int dhcp_fd;
 };
@@ -332,9 +314,7 @@ int ipv6_staticdadcompleted(const struct interface *);
 int ipv6_startstatic(struct interface *);
 ssize_t ipv6_env(char **, const char *, const struct interface *);
 void ipv6_ctxfree(struct dhcpcd_ctx *);
-int ipv6_handlert(struct dhcpcd_ctx *, int cmd, const struct rt6 *);
-void ipv6_freerts(struct rt6_head *);
-void ipv6_buildroutes(struct dhcpcd_ctx *);
+bool inet6_getroutes(struct dhcpcd_ctx *, struct rt_head *);
 
 #else
 #define ipv6_init(a) (NULL)
index 1bcd0e7c7b804f884d6a5774240c7cde84a9cb9e..be81c9543ca004fe9ac7269a00a387904c3f2ea9 100644 (file)
--- a/ipv6nd.c
+++ b/ipv6nd.c
@@ -49,6 +49,7 @@
 #include "if.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
+#include "route.h"
 #include "script.h"
 
 /* Debugging Router Solicitations is a lot of spam, so disable it */
@@ -356,7 +357,7 @@ ipv6nd_expire(struct interface *ifp, uint32_t seconds)
        if (seconds)
                ipv6nd_expirera(ifp);
        else
-               ipv6_buildroutes(ifp->ctx);
+               rt_build(ifp->ctx, AF_INET6);
 }
 
 static void
@@ -369,7 +370,7 @@ ipv6nd_reachable(struct ra *rap, int flags)
                            "%s: %s is reachable again",
                            rap->iface->name, rap->sfrom);
                        rap->expired = 0;
-                       ipv6_buildroutes(rap->iface->ctx);
+                       rt_build(rap->iface->ctx, AF_INET6);
                        /* XXX Not really an RA */
                        script_runreason(rap->iface, "ROUTERADVERT");
                }
@@ -379,7 +380,7 @@ ipv6nd_reachable(struct ra *rap, int flags)
                            "%s: %s is unreachable, expiring it",
                            rap->iface->name, rap->sfrom);
                        rap->expired = 1;
-                       ipv6_buildroutes(rap->iface->ctx);
+                       rt_build(rap->iface->ctx, AF_INET6);
                        /* XXX Not really an RA */
                        script_runreason(rap->iface, "ROUTERADVERT");
                }
@@ -1104,9 +1105,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
        /* Find any freshly added routes, such as the subnet route.
         * We do this because we cannot rely on recieving the kernel
         * notification right now via our link socket. */
-       if_initrt6(ifp->ctx);
+       if_initrt(ifp->ctx);
 
-       ipv6_buildroutes(ifp->ctx);
+       rt_build(ifp->ctx, AF_INET6);
        if (ipv6nd_scriptrun(rap))
                return;
 
@@ -1432,7 +1433,7 @@ ipv6nd_expirera(void *arg)
                eloop_timeout_add_tv(ifp->ctx->eloop,
                    &next, ipv6nd_expirera, ifp);
        if (expired) {
-               ipv6_buildroutes(ifp->ctx);
+               rt_build(ifp->ctx, AF_INET6);
                script_runreason(ifp, "ROUTERADVERT");
        }
 
@@ -1465,7 +1466,7 @@ ipv6nd_drop(struct interface *ifp)
                        TAILQ_REMOVE(&rtrs, rap, next);
                        ipv6nd_drop_ra(rap);
                }
-               ipv6_buildroutes(ifp->ctx);
+               rt_build(ifp->ctx, AF_INET6);
                if ((ifp->options->options & DHCPCD_NODROP) != DHCPCD_NODROP)
                        script_runreason(ifp, "ROUTERADVERT");
        }
@@ -1538,7 +1539,7 @@ ipv6nd_handlena(struct dhcpcd_ctx *dctx, struct interface *ifp,
                logger(ifp->ctx, LOG_INFO, "%s: %s not a router (%s)",
                    ifp->name, taddr, ctx->sfrom);
                rap->expired = 1;
-               ipv6_buildroutes(ifp->ctx);
+               rt_build(ifp->ctx,  AF_INET6);
                script_runreason(ifp, "ROUTERADVERT");
                return;
        }
@@ -1548,7 +1549,7 @@ ipv6nd_handlena(struct dhcpcd_ctx *dctx, struct interface *ifp,
                        rap->expired = 0;
                        logger(ifp->ctx, LOG_INFO, "%s: %s reachable (%s)",
                            ifp->name, taddr, ctx->sfrom);
-                       ipv6_buildroutes(ifp->ctx);
+                       rt_build(ifp->ctx, AF_INET6);
                        script_runreason(rap->iface, "ROUTERADVERT"); /* XXX */
                }
        }
diff --git a/route.c b/route.c
new file mode 100644 (file)
index 0000000..2e2f8db
--- /dev/null
+++ b/route.c
@@ -0,0 +1,530 @@
+/*
+ * dhcpcd - route management
+ * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "if.h"
+#include "ipv4.h"
+#include "ipv4ll.h"
+#include "ipv6.h"
+#include "route.h"
+#include "sa.h"
+
+void
+rt_init(struct dhcpcd_ctx *ctx)
+{
+
+       TAILQ_INIT(&ctx->routes);
+       TAILQ_INIT(&ctx->kroutes);
+       TAILQ_INIT(&ctx->froutes);
+}
+
+static void
+rt_desc(const char *cmd, const struct rt *rt)
+{
+       char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
+       int prefix;
+       struct dhcpcd_ctx *ctx;
+       const char *ifname;
+       bool gateway_unspec;
+
+       assert(cmd != NULL);
+       assert(rt != NULL);
+       assert(rt->rt_ifp != NULL);
+
+       ctx = rt->rt_ifp->ctx;
+       ifname = rt->rt_ifp->name;
+       sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
+       prefix = sa_toprefix(&rt->rt_netmask);
+       sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
+
+       gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
+
+       if (rt->rt_flags & RTF_HOST) {
+               if (gateway_unspec)
+                       logger(ctx, LOG_INFO, "%s: %s host route to %s",
+                           ifname, cmd, dest);
+               else
+                       logger(ctx, LOG_INFO, "%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))
+       {
+               if (gateway_unspec)
+                       logger(ctx, LOG_INFO, "%s: %s default route",
+                           ifname, cmd);
+               else
+                       logger(ctx, LOG_INFO, "%s: %s default route via %s",
+                           ifname, cmd, gateway);
+       } else if (gateway_unspec)
+               logger(ctx, LOG_INFO, "%s: %s route to %s/%d",
+                   ifname, cmd, dest, prefix);
+       else
+               logger(ctx, LOG_INFO, "%s: %s route to %s/%d via %s",
+                   ifname, cmd, dest, prefix, gateway);
+}
+
+void
+rt_headclear(struct rt_head *rts)
+{
+       struct rt *rt;
+       struct dhcpcd_ctx *ctx;
+
+       if (rts == NULL)
+               return;
+
+       ctx = NULL;
+       while ((rt = TAILQ_FIRST(rts))) {
+               TAILQ_REMOVE(rts, rt, rt_next);
+               if (ctx == NULL) {
+                       ctx = rt->rt_ifp->ctx;
+                       assert(&ctx->froutes != rts);
+               }
+               TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next);
+       }
+}
+
+static void
+rt_headfree(struct rt_head *rts)
+{
+       struct rt *rt;
+
+       while ((rt = TAILQ_FIRST(rts))) {
+               TAILQ_REMOVE(rts, rt, rt_next);
+               free(rt);
+       }
+}
+
+void
+rt_dispose(struct dhcpcd_ctx *ctx)
+{
+
+       assert(ctx != NULL);
+       rt_headfree(&ctx->routes);
+       rt_headfree(&ctx->kroutes);
+       rt_headfree(&ctx->froutes);
+}
+
+struct rt *
+rt_new(struct interface *ifp)
+{
+       struct rt *rt;
+       struct dhcpcd_ctx *ctx;
+
+       assert(ifp != NULL);
+       ctx = ifp->ctx;
+       if ((rt = TAILQ_FIRST(&ctx->froutes)) != NULL)
+               TAILQ_REMOVE(&ctx->froutes, rt, rt_next);
+       else if ((rt = malloc(sizeof(*rt))) == NULL) {
+               logger(ctx, LOG_ERR, "%s: %m", __func__);
+               return NULL;
+       }
+       memset(rt, 0, sizeof(*rt));
+       rt->rt_ifp = ifp;
+#ifdef HAVE_ROUTE_METRIC
+       rt->rt_metric = ifp->metric;
+#endif
+       return rt;
+}
+
+void
+rt_free(struct rt *rt)
+{
+
+       assert(rt != NULL);
+       assert(rt->rt_ifp->ctx != NULL);
+       TAILQ_INSERT_TAIL(&rt->rt_ifp->ctx->froutes, rt, rt_next);
+}
+
+void
+rt_freeif(struct interface *ifp)
+{
+       struct dhcpcd_ctx *ctx;
+       struct rt *rt, *rtn;
+
+       if (ifp == NULL)
+               return;
+       ctx = ifp->ctx;
+       TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) {
+               if (rt->rt_ifp == ifp) {
+                       TAILQ_REMOVE(&ctx->routes, rt, rt_next);
+                       rt_free(rt);
+               }
+       }
+       TAILQ_FOREACH_SAFE(rt, &ctx->kroutes, rt_next, rtn) {
+               if (rt->rt_ifp == ifp) {
+                       TAILQ_REMOVE(&ctx->kroutes, rt, rt_next);
+                       rt_free(rt);
+               }
+       }
+}
+
+struct rt *
+rt_find(struct rt_head *rts, const struct rt *f)
+{
+       struct rt *rt;
+
+       assert(rts != NULL);
+       assert(f != NULL);
+       TAILQ_FOREACH(rt, rts, rt_next) {
+               if (sa_cmp(&rt->rt_dest, &f->rt_dest) == 0 &&
+#ifdef HAVE_ROUTE_METRIC
+                   rt->rt_ifp->metric == f->rt_ifp->metric &&
+#endif
+                   sa_cmp(&rt->rt_netmask, &f->rt_netmask) == 0)
+                       return rt;
+       }
+       return NULL;
+}
+
+static void
+rt_kfree(struct rt *rt)
+{
+       struct dhcpcd_ctx *ctx;
+       struct rt *f;
+
+       assert(rt != NULL);
+       ctx = rt->rt_ifp->ctx;
+       if ((f = rt_find(&ctx->kroutes, rt)) != NULL) {
+               TAILQ_REMOVE(&ctx->kroutes, f, rt_next);
+               rt_free(f);
+       }
+}
+
+/* If something other than dhcpcd removes a route,
+ * we need to remove it from our internal table. */
+void
+rt_recvrt(int cmd, const struct rt *rt)
+{
+       struct dhcpcd_ctx *ctx;
+       struct rt *f;
+
+       assert(rt != NULL);
+       ctx = rt->rt_ifp->ctx;
+       f = rt_find(&ctx->kroutes, rt);
+
+       switch(cmd) {
+       case RTM_DELETE:
+               if (f != NULL) {
+                       TAILQ_REMOVE(&ctx->kroutes, f, rt_next);
+                       rt_free(f);
+               }
+               if ((f = rt_find(&ctx->routes, rt)) != NULL) {
+                       TAILQ_REMOVE(&ctx->routes, f, rt_next);
+                       rt_desc("deleted", f);
+                       rt_free(f);
+               }
+               break;
+       case RTM_ADD:
+               if (f != NULL)
+                       break;
+               if ((f = rt_new(rt->rt_ifp)) == NULL)
+                       break;
+               memcpy(f, rt, sizeof(*f));
+               TAILQ_INSERT_TAIL(&ctx->kroutes, f, rt_next);
+               break;
+       }
+
+#ifdef HAVE_ROUTE_METRIC
+       if (rt->rt_dest.sa_family == AF_INET)
+               ipv4ll_recvrt(cmd, rt);
+#endif
+}
+
+static bool
+rt_add(struct rt *nrt, struct rt *ort)
+{
+       struct dhcpcd_ctx *ctx;
+       bool change;
+
+       assert(nrt != NULL);
+       ctx = nrt->rt_ifp->ctx;
+
+       /* Don't set default routes if not asked to */
+       if (!(nrt->rt_ifp->options->options & DHCPCD_GATEWAY) &&
+           sa_is_unspecified(&nrt->rt_dest) &&
+           sa_is_unspecified(&nrt->rt_netmask))
+               return false;
+
+       rt_desc(ort == NULL ? "adding" : "changing", nrt);
+
+       change = false;
+       if (ort == NULL) {
+               ort = rt_find(&ctx->kroutes, nrt);
+               if (ort != NULL &&
+                   ((ort->rt_flags & RTF_REJECT &&
+                     nrt->rt_flags & RTF_REJECT) ||
+                    (ort->rt_ifp == nrt->rt_ifp &&
+#ifdef HAVE_ROUTE_METRIC
+                   ort->rt_metric == nrt->rt_metric &&
+#endif
+                   sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
+               {
+                       if (ort->rt_mtu == nrt->rt_mtu)
+                               return true;
+                       change = true;
+               }
+       } else if (ort->rt_dflags & RTDF_FAKE &&
+           !(nrt->rt_dflags & RTDF_FAKE) &&
+           ort->rt_ifp == nrt->rt_ifp &&
+#ifdef HAVE_ROUTE_METRIC
+           ort->rt_metric == nrt->rt_metric &&
+#endif
+           sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
+           sa_cmp(&ort->rt_netmask, &nrt->rt_netmask) == 0 &&
+           sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
+       {
+               if (ort->rt_mtu == nrt->rt_mtu)
+                       return 0;
+               change = true;
+       }
+
+#ifdef RTF_CLONING
+       /* BSD can set routes to be cloning routes.
+        * Cloned routes inherit the parent flags.
+        * As such, we need to delete and re-add the route to flush children
+        * to correct the flags. */
+       if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
+               change = true;
+#endif
+
+       if (change) {
+               if (if_route(RTM_CHANGE, nrt) != -1)
+                       return true;
+               if (errno != ESRCH)
+                       logger(ctx, LOG_ERR, "if_route (CHG): %m");
+       }
+
+#ifdef HAVE_ROUTE_METRIC
+       /* With route metrics, we can safely add the new route before
+        * deleting the old route. */
+       if (if_route(RTM_ADD, nrt) != -1) {
+               if (ort != NULL) {
+                       if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
+                               logger(ctx, LOG_ERR, "if_route (DEL): %m");
+                       rt_kfree(ort);
+               }
+               return true;
+       }
+
+       /* If the kernel claims the route exists we need to rip out the
+        * old one first. */
+       if (errno != EEXIST || ort == NULL)
+               goto logerr;
+#endif
+
+       /* No route metrics, we need to delete the old route before
+        * adding the new one. */
+#ifdef ROUTE_PER_GATEWAY
+       errno = 0;
+#endif
+       if (ort != NULL) {
+               if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
+                       logger(ctx, LOG_ERR, "if_route (DEL): %m");
+               else
+                       rt_kfree(ort);
+       }
+#ifdef ROUTE_PER_GATEWAY
+       /* The OS allows many routes to the same dest with different gateways.
+        * dhcpcd does not support this yet, so for the time being just keep on
+        * deleting the route until there is an error. */
+       if (ort != NULL && errno == 0) {
+               for (;;) {
+                       if (if_route(RTM_DELETE, ort) == -1)
+                               break;
+               }
+       }
+#endif
+       if (if_route(RTM_ADD, nrt) != -1)
+               return true;
+#ifdef HAVE_ROUTE_METRIC
+logerr:
+#endif
+       logger(ctx, LOG_ERR, "if_route (ADD): %m");
+       return false;
+}
+
+static bool
+rt_delete(struct rt *rt)
+{
+       int retval;
+
+       rt_desc("deleting", rt);
+       retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
+       if (!retval && errno != ENOENT && errno != ESRCH)
+               logger(rt->rt_ifp->ctx, LOG_ERR,
+                   "%s: if_delroute: %m", rt->rt_ifp->name);
+       /* Remove the route from our kernel table so we can add a
+        * IPv4LL default route if possible. */
+       else
+               rt_kfree(rt);
+       return retval;
+}
+
+static bool
+rt_cmp(const struct rt *r1, const struct rt *r2)
+{
+
+       return (r1->rt_ifp == r2->rt_ifp &&
+#ifdef HAVE_ROUTE_METRIC
+           r1->rt_metric == r2->rt_metric &&
+#endif
+           sa_cmp(&r1->rt_gateway, &r1->rt_gateway) == 0);
+}
+
+static bool
+rt_doroute(struct rt *rt)
+{
+       struct dhcpcd_ctx *ctx;
+       struct rt *or;
+
+       ctx = rt->rt_ifp->ctx;
+       /* Do we already manage it? */
+       if ((or = rt_find(&ctx->routes, rt))) {
+               if (rt->rt_dflags & RTDF_FAKE)
+                       return true;
+               if (or->rt_dflags & RTDF_FAKE ||
+                   !rt_cmp(rt,or) ||
+                   (rt->rt_ifa.sa_family != AF_UNSPEC &&
+                   sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
+                   or->rt_mtu != rt->rt_mtu)
+               {
+                       if (!rt_add(rt, or))
+                               return false;
+               }
+               TAILQ_REMOVE(&ctx->routes, or, rt_next);
+               rt_free(or);
+       } else {
+               if (rt->rt_dflags & RTDF_FAKE) {
+                       if ((or = rt_find(&ctx->kroutes, rt)) == NULL)
+                               return false;
+                       if (!rt_cmp(rt, or))
+                               return false;
+               } else {
+                       if (!rt_add(rt, NULL))
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+void
+rt_build(struct dhcpcd_ctx *ctx, int af)
+{
+       struct rt_head routes, added;
+       struct rt *rt, *rtn;
+       unsigned long long o;
+#ifdef INET6
+       bool have_default = false;
+#endif
+
+       /* We need to have the interfaces in the correct order to ensure
+        * our routes are managed correctly. */
+       if_sortinterfaces(ctx);
+
+       TAILQ_INIT(&routes);
+       TAILQ_INIT(&added);
+
+       switch (af) {
+#ifdef INET
+       case AF_INET:
+               if (!inet_getroutes(ctx, &routes))
+                       return;
+               break;
+#endif
+#ifdef INET6
+       case AF_INET6:
+               if (!inet6_getroutes(ctx, &routes))
+                       return;
+               break;
+#endif
+       }
+
+       TAILQ_FOREACH_SAFE(rt, &routes, rt_next, rtn) {
+               if (rt->rt_dest.sa_family != af &&
+                   rt->rt_gateway.sa_family != af)
+                       continue;
+               /* Is this route already in our table? */
+               if ((rt_find(&added, rt)) != NULL)
+                       continue;
+               if (rt_doroute(rt)) {
+                       TAILQ_REMOVE(&routes, rt, rt_next);
+                       TAILQ_INSERT_TAIL(&added, rt, rt_next);
+#ifdef INET6
+                       if (sa_is_unspecified(&rt->rt_dest) &&
+                           sa_is_unspecified(&rt->rt_netmask))
+                               have_default = true;
+#endif
+               }
+       }
+
+       /* Remove old routes we used to manage
+        * If we own the default route, but not RA management itself
+        * then we need to preserve the last best default route we had */
+       TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) {
+               if (rt->rt_dest.sa_family != af &&
+                   rt->rt_gateway.sa_family != af)
+                       continue;
+               TAILQ_REMOVE(&ctx->routes, rt, rt_next);
+               if (rt_find(&added, rt) == NULL) {
+                       o = rt->rt_ifp->options ?
+                           rt->rt_ifp->options->options :
+                           ctx->options;
+#ifdef INET6
+                       if (!have_default &&
+                           (o & DHCPCD_IPV6RA_OWN_DEFAULT) &&
+                           !(o & DHCPCD_IPV6RA_OWN) &&
+                           rt->rt_dest.sa_family == AF_INET6 &&
+                           sa_is_unspecified(&rt->rt_dest))
+                               have_default = true;
+                               /* no need to add it back to our routing table
+                                * as we delete an exiting route when we add
+                                * a new one */
+                       else
+#endif
+                       if ((o &
+                               (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
+                               (DHCPCD_EXITING | DHCPCD_PERSISTENT))
+                               rt_delete(rt);
+               }
+               TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next);
+       }
+
+       rt_headclear(&ctx->routes);
+       TAILQ_CONCAT(&ctx->routes, &added, rt_next);
+       rt_headclear(&routes);
+}
diff --git a/route.h b/route.h
new file mode 100644 (file)
index 0000000..0c7ecc0
--- /dev/null
+++ b/route.h
@@ -0,0 +1,92 @@
+/*
+ * dhcpcd - route management
+ * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * rEDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ROUTE_H
+#define ROUTE_H
+
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <stdbool.h>
+
+#include "dhcpcd.h"
+
+/* Some systems have route metrics.
+ * OpenBSD route priority is not this. */
+#ifndef HAVE_ROUTE_METRIC
+# if defined(__linux__)
+#  define HAVE_ROUTE_METRIC 1
+# endif
+#endif
+
+#if defined(__OpenBSD__) || defined (__sun)
+#  define ROUTE_PER_GATEWAY
+/* XXX dhcpcd doesn't really support this yet.
+ * But that's generally OK if only dhcpcd is managing routes. */
+#endif
+
+union sa_ss {
+       struct sockaddr         sa;
+       struct sockaddr_in      sin;
+       struct sockaddr_in6     sin6;
+};
+
+struct rt {
+       TAILQ_ENTRY(rt)         rt_next;
+       union sa_ss             rt_ss_dest;
+#define rt_dest                        rt_ss_dest.sa
+       union sa_ss             rt_ss_netmask;
+#define rt_netmask             rt_ss_netmask.sa
+       union sa_ss             rt_ss_gateway;
+#define rt_gateway             rt_ss_gateway.sa
+       struct interface        *rt_ifp;
+       union sa_ss             rt_ss_ifa;
+#define rt_ifa                 rt_ss_ifa.sa
+       unsigned int            rt_flags;
+       unsigned int            rt_mtu;
+#ifdef HAVE_ROUTE_METRIC
+       unsigned int            rt_metric;
+#endif
+       unsigned int            rt_dflags;
+#define RTDF_INIT              0x01            /* Generated by if_initrt() */
+#define RTDF_IFA_ROUTE         0x02            /* Address generated route */
+#define RTDF_FAKE              0x04            /* Maybe us on lease reboot  */
+};
+TAILQ_HEAD(rt_head, rt);
+
+void rt_init(struct dhcpcd_ctx *);
+void rt_dispose(struct dhcpcd_ctx *);
+struct rt * rt_find(struct rt_head *, const struct rt *);
+void rt_free(struct rt *);
+void rt_freeif(struct interface *);
+void rt_headclear(struct rt_head *);
+void rt_headfreeif(struct rt_head *);
+struct rt * rt_new(struct interface *);
+void rt_recvrt(int, const struct rt *);
+void rt_build(struct dhcpcd_ctx *, int);
+
+#endif
diff --git a/sa.c b/sa.c
new file mode 100644 (file)
index 0000000..66eda8d
--- /dev/null
+++ b/sa.c
@@ -0,0 +1,407 @@
+/*
+ * Socket Address handling for dhcpcd
+ * Copyright (c) 2015-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#ifdef AF_LINK
+#include <net/if_dl.h>
+#elif AF_PACKET
+#include <linux/if_packet.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include "common.h"
+#include "sa.h"
+
+socklen_t
+sa_addroffset(const struct sockaddr *sa)
+{
+
+       assert(sa != NULL);
+       switch(sa->sa_family) {
+#ifdef INET
+       case AF_INET:
+               return offsetof(struct sockaddr_in, sin_addr) +
+                      offsetof(struct in_addr, s_addr);
+#endif /* INET */
+#ifdef INET6
+       case AF_INET6:
+               return offsetof(struct sockaddr_in6, sin6_addr) +
+                      offsetof(struct in6_addr, s6_addr);
+#endif /* INET6 */
+       default:
+               errno = EAFNOSUPPORT;
+               return 0;
+       }
+}
+
+socklen_t
+sa_addrlen(const struct sockaddr *sa)
+{
+#define membersize(type, member) sizeof(((type *)0)->member)
+       assert(sa != NULL);
+       switch(sa->sa_family) {
+#ifdef INET
+       case AF_INET:
+               return membersize(struct in_addr, s_addr);
+#endif /* INET */
+#ifdef INET6
+       case AF_INET6:
+               return membersize(struct in6_addr, s6_addr);
+#endif /* INET6 */
+       default:
+               errno = EAFNOSUPPORT;
+               return 0;
+       }
+}
+
+bool
+sa_is_unspecified(const struct sockaddr *sa)
+{
+
+       assert(sa != NULL);
+       switch(sa->sa_family) {
+       case AF_UNSPEC:
+               return true;
+#ifdef INET
+       case AF_INET:
+               return satocsin(sa)->sin_addr.s_addr == INADDR_ANY;
+#endif /* INET */
+#ifdef INET6
+       case AF_INET6:
+               return IN6_IS_ADDR_UNSPECIFIED(&satocsin6(sa)->sin6_addr);
+#endif /* INET6 */
+       default:
+               errno = EAFNOSUPPORT;
+               return false;
+       }
+}
+
+#ifdef INET6
+#ifndef IN6MASK128
+#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
+#endif
+static const struct in6_addr in6allones = IN6MASK128;
+#endif
+
+bool
+sa_is_allones(const struct sockaddr *sa)
+{
+
+       assert(sa != NULL);
+       switch(sa->sa_family) {
+       case AF_UNSPEC:
+               return false;
+#ifdef INET
+       case AF_INET:
+       {
+               const struct sockaddr_in *sin;
+
+               sin = satocsin(sa);
+               return sin->sin_addr.s_addr == INADDR_BROADCAST;
+       }
+#endif /* INET */
+#ifdef INET6
+       case AF_INET6:
+       {
+               const struct sockaddr_in6 *sin6;
+
+               sin6 = satocsin6(sa);
+               return IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &in6allones);
+       }
+#endif /* INET6 */
+       default:
+               errno = EAFNOSUPPORT;
+               return false;
+       }
+}
+
+bool
+sa_is_loopback(const struct sockaddr *sa)
+{
+
+       assert(sa != NULL);
+       switch(sa->sa_family) {
+       case AF_UNSPEC:
+               return false;
+#ifdef INET
+       case AF_INET:
+       {
+               const struct sockaddr_in *sin;
+
+               sin = satocsin(sa);
+               return sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK);
+       }
+#endif /* INET */
+#ifdef INET6
+       case AF_INET6:
+       {
+               const struct sockaddr_in6 *sin6;
+
+               sin6 = satocsin6(sa);
+               return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);
+       }
+#endif /* INET6 */
+       default:
+               errno = EAFNOSUPPORT;
+               return false;
+       }
+}
+
+int
+sa_toprefix(const struct sockaddr *sa)
+{
+
+       assert(sa != NULL);
+       switch(sa->sa_family) {
+#ifdef INET
+       case AF_INET:
+       {
+               const struct sockaddr_in *sin;
+               int mask;
+               int cidr;
+
+               sin = satocsin(sa);
+               mask = (int)ntohl(sin->sin_addr.s_addr);
+               cidr = 33 - ffs(mask);          /* 33 - (1 .. 32) -> 32 .. 1 */
+               if (cidr < 32) {                /* more than 1 bit in mask */
+                       /* check for non-contig netmask */
+                       if ((mask ^ (((1 << cidr) - 1) << (32 - cidr))) != 0) {
+                               errno = EINVAL;
+                               return -1;      /* noncontig, no pfxlen */
+                       }
+               }
+               return cidr;
+       }
+#endif
+#ifdef INET6
+       case AF_INET6:
+       {
+               const struct sockaddr_in6 *sin6;
+               int x, y;
+               const uint8_t *lim, *p;
+
+               sin6 = satocsin6(sa);
+               p = (const uint8_t *)sin6->sin6_addr.s6_addr;
+               lim = p + sizeof(sin6->sin6_addr.s6_addr);
+               for (x = 0; p < lim; x++, p++) {
+                       if (*p != 0xff)
+                               break;
+               }
+               y = 0;
+               if (p < lim) {
+                       for (y = 0; y < NBBY; y++) {
+                               if ((*p & (0x80 >> y)) == 0)
+                                       break;
+                       }
+               }
+
+               /*
+                * when the limit pointer is given, do a stricter check on the
+                * remaining bits.
+                */
+               if (p < lim) {
+                       if (y != 0 && (*p & (0x00ff >> y)) != 0)
+                               return 0;
+                       for (p = p + 1; p < lim; p++)
+                               if (*p != 0)
+                                       return 0;
+               }
+
+               return (uint8_t)(x * NBBY + y);
+       }
+#endif
+       default:
+               errno = EAFNOSUPPORT;
+               return -1;
+       }
+}
+
+int
+sa_fromprefix(struct sockaddr *sa, int prefix)
+{
+       uint8_t *ap, a;
+       int max_prefix, i;
+
+       switch (sa->sa_family) {
+#ifdef INET
+       case AF_INET:
+               max_prefix = 32;
+               break;
+#endif
+#ifdef INET6
+       case AF_INET6:
+               max_prefix = 128;
+               break;
+#endif
+       default:
+               errno = EAFNOSUPPORT;
+               return -1;
+       }
+
+       ap = (uint8_t *)sa + sa_addroffset(sa);
+       for (i = 0; i < (prefix / NBBY); i++)
+               *ap++ = 0xff;
+       a = 0xff;
+       a  = (uint8_t)(a << (8 - (prefix % NBBY)));
+       *ap = a;
+       for (i = 0; i < ((max_prefix - prefix) / NBBY); i++)
+               *ap++ = 0x00;
+       return 0;
+}
+
+/* inet_ntop, but for sockaddr. */
+const char *
+sa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len)
+{
+       const void *addr;
+
+       assert(buf != NULL);
+#ifdef AF_LINK
+#ifndef CLLADDR
+#define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
+#endif
+       if (sa->sa_family == AF_LINK) {
+               const struct sockaddr_dl *sdl;
+
+               sdl = (const void *)sa;
+               if (sdl->sdl_alen == 0) {
+                       if (snprintf(buf, len, "link#%d", sdl->sdl_index) == -1)
+                               return NULL;
+                       return buf;
+               }
+               return hwaddr_ntoa(CLLADDR(sdl), sdl->sdl_alen, buf, len);
+       }
+#elif AF_PACKET
+       if (sa->sa_family == AF_PACKET) {
+               const struct sockaddr_ll *sll;
+
+               sll = (const void *)sa;
+               return hwaddr_ntoa(sll->sll_addr, sll->sll_halen, buf, len);
+       }
+#endif
+       addr = (const char *)sa + sa_addroffset(sa);
+       return inet_ntop(sa->sa_family, addr, buf, len);
+}
+
+int
+sa_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+       socklen_t offset, len;
+
+       assert(sa1 != NULL);
+       assert(sa2 != NULL);
+
+       /* Treat AF_UNSPEC as the unspecified address. */
+       if ((sa1->sa_family == AF_UNSPEC || sa2->sa_family == AF_UNSPEC) &&
+           sa_is_unspecified(sa1) && sa_is_unspecified(sa2))
+               return 0;
+
+       if (sa1->sa_family != sa2->sa_family)
+               return sa1->sa_family - sa2->sa_family;
+
+#ifdef HAVE_SA_LEN
+       len = MIN(sa1->sa_len, sa2->sa_len);
+#endif
+
+       switch (sa1->sa_family) {
+#ifdef INET
+       case AF_INET:
+               offset = offsetof(struct sockaddr_in, sin_addr);
+#ifdef HAVE_SA_LEN
+               len -= offset;
+#else
+               len = sizeof(struct in_addr);
+#endif
+               break;
+#endif
+#ifdef INET6
+       case AF_INET6:
+               offset = offsetof(struct sockaddr_in6, sin6_addr);
+#ifdef HAVE_SA_LEN
+               len -= offset;
+#else
+               len = sizeof(struct in6_addr);
+#endif
+               break;
+#endif
+       default:
+               offset = 0;
+#ifndef HAVE_SA_LEN
+               len = sizeof(struct sockaddr);
+#endif
+               break;
+       }
+
+       return memcmp((const char *)sa1 + offset,
+           (const char *)sa2 + offset,
+           len);
+}
+
+#ifdef INET
+void
+sa_in_init(struct sockaddr *sa, const struct in_addr *addr)
+{
+       struct sockaddr_in *sin;
+
+       assert(sa != NULL);
+       assert(addr != NULL);
+       sin = satosin(sa);
+       sin->sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+       sin->sin_len = sizeof(*sin);
+#endif
+       sin->sin_addr.s_addr = addr->s_addr;
+}
+#endif
+
+#ifdef INET6
+void
+sa_in6_init(struct sockaddr *sa, const struct in6_addr *addr)
+{
+       struct sockaddr_in6 *sin6;
+
+       assert(sa != NULL);
+       assert(addr != NULL);
+       sin6 = satosin6(sa);
+       sin6->sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+       sin6->sin6_len = sizeof(*sin6);
+#endif
+       memcpy(&sin6->sin6_addr.s6_addr, &addr->s6_addr,
+           sizeof(sin6->sin6_addr.s6_addr));
+}
+#endif
diff --git a/sa.h b/sa.h
new file mode 100644 (file)
index 0000000..f958da2
--- /dev/null
+++ b/sa.h
@@ -0,0 +1,62 @@
+/*
+ * Socket Address handling for dhcpcd
+ * Copyright (c) 2015-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SA_H
+#define SA_H
+
+#include <sys/socket.h>
+
+#ifndef __linux__
+#define HAVE_SA_LEN
+#endif
+
+/* Allow for a sockaddr_dl being printed too. */
+#define INET_MAX_ADDRSTRLEN    (20 * 3)
+
+#ifdef INET
+#define satosin(sa) ((struct sockaddr_in *)(void *)(sa))
+#define satocsin(sa) ((const struct sockaddr_in *)(const void *)(sa))
+#endif
+#ifdef INET6
+#define satosin6(sa) ((struct sockaddr_in6 *)(void *)(sa))
+#define satocsin6(sa) ((const struct sockaddr_in6 *)(const void *)(sa))
+#endif
+
+socklen_t sa_addroffset(const struct sockaddr *sa);
+socklen_t sa_addrlen(const struct sockaddr *sa);
+bool sa_is_unspecified(const struct sockaddr *);
+bool sa_is_allones(const struct sockaddr *);
+bool sa_is_loopback(const struct sockaddr *);
+void *sa_toaddr(struct sockaddr *);
+int sa_toprefix(const struct sockaddr *);
+int sa_fromprefix(struct sockaddr *, int);
+const char *sa_addrtop(const struct sockaddr *, char *, socklen_t);
+int sa_cmp(const struct sockaddr *, const struct sockaddr *);
+void sa_in_init(struct sockaddr *, const struct in_addr *);
+void sa_in6_init(struct sockaddr *, const struct in6_addr *);
+
+#endif