From: Roy Marples Date: Tue, 25 Oct 2016 15:33:02 +0000 (+0000) Subject: Remove address family specific routing and introduce address family X-Git-Tag: v7.0.0-beta1~129 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9aa114876fd6e041abd80af801f1f5b04b8bfc0b;p=thirdparty%2Fdhcpcd.git Remove address family specific routing and introduce address family agnostic routing. This makes the platform drivers simpler and reduces some code duplication. Needs a lot of testing. --- diff --git a/Makefile b/Makefile index 9d825719..0f01ce80 100644 --- 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 3d1293b2..0a475e16 100644 --- 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 a21eea30..931fa377 100644 --- 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 389263cd..475d5468 100644 --- 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); diff --git a/dhcpcd.c b/dhcpcd.c index 33dfe05e..e5facde4 100644 --- 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); diff --git a/dhcpcd.h b/dhcpcd.h index a41360d1..aa1ede6a 100644 --- 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; diff --git a/if-bsd.c b/if-bsd.c index 51aa8449..40df9a37 100644 --- 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 * All rights reserved @@ -57,6 +57,7 @@ # include #endif +#include #include #include #include @@ -82,6 +83,8 @@ #include "ipv4ll.h" #include "ipv6.h" #include "ipv6nd.h" +#include "route.h" +#include "sa.h" #include "bpf-filter.h" @@ -91,20 +94,9 @@ #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 || diff --git a/if-linux.c b/if-linux.c index 4836d72d..54c740e6 100644 --- a/if-linux.c +++ b/if-linux.c @@ -1,5 +1,5 @@ /* - * dhcpcd - DHCP client daemon + * Linux interface driver for dhcpcd * Copyright (c) 2006-2016 Roy Marples * 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 @@ -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) diff --git a/if-options.c b/if-options.c index f08a7a87..c282adde 100644 --- a/if-options.c +++ b/if-options.c @@ -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); diff --git a/if-options.h b/if-options.h index 991672a0..bbd26eab 100644 --- a/if-options.h +++ b/if-options.h @@ -38,6 +38,7 @@ #include #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; diff --git a/if-sun.c b/if-sun.c index 602f824e..3aeeb20b 100644 --- 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 fcfdf3da..ac221f71 100644 --- 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 d2b757ae..a8555a43 100644 --- a/if.h +++ b/if.h @@ -35,20 +35,6 @@ #include /* 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 @@ -104,6 +91,12 @@ #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 876ca0af..2f64f382 100644 --- 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 c24a38ef..335445f7 100644 --- a/ipv4.h +++ b/ipv4.h @@ -72,22 +72,6 @@ (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 diff --git a/ipv4ll.c b/ipv4ll.c index 77e678aa..1b6ce4e7 100644 --- a/ipv4ll.c +++ b/ipv4ll.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -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; } } diff --git a/ipv4ll.h b/ipv4ll.h index 34a185ff..6ab659ce 100644 --- 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 ff48c44a..4514bb98 100644 --- 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 8182d34a..602258f7 100644 --- 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) diff --git a/ipv6nd.c b/ipv6nd.c index 1bcd0e7c..be81c954 100644 --- 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 index 00000000..2e2f8dba --- /dev/null +++ b/route.c @@ -0,0 +1,530 @@ +/* + * dhcpcd - route management + * Copyright (c) 2006-2016 Roy Marples + * 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 +#include +#include +#include +#include +#include +#include + +#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 index 00000000..0c7ecc07 --- /dev/null +++ b/route.h @@ -0,0 +1,92 @@ +/* + * dhcpcd - route management + * Copyright (c) 2006-2016 Roy Marples + * 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 +#include + +#include + +#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 index 00000000..66eda8dd --- /dev/null +++ b/sa.c @@ -0,0 +1,407 @@ +/* + * Socket Address handling for dhcpcd + * Copyright (c) 2015-2016 Roy Marples + * 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 + +#include +#ifdef AF_LINK +#include +#elif AF_PACKET +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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 index 00000000..f958da29 --- /dev/null +++ b/sa.h @@ -0,0 +1,62 @@ +/* + * Socket Address handling for dhcpcd + * Copyright (c) 2015-2016 Roy Marples + * 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 + +#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