agnostic routing.
This makes the platform drivers simpler and reduces some code duplication.
Needs a lot of testing.
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=
#include "if.h"
#include "ipv4.h"
#include "ipv4ll.h"
+#include "sa.h"
#include "script.h"
#define DAD "Duplicate address detected"
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 *
/* 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))
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);
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++;
}
}
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
/* 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 */
#endif
dhcp_close(ifp);
+ state->state = DHS_INIT;
free(state->offer);
state->offer = NULL;
state->offer_len = 0;
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__);
}
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;
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 *);
if (state) {
ipv6_freedrop_addrs(&state->addrs, drop, ifd);
if (drop)
- ipv6_buildroutes(ifp->ctx);
+ rt_build(ifp->ctx, AF_INET6);
}
}
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);
}
}
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;
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);
} 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;
}
}
}
}
+ rt_init(&ctx);
+
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
if (ifp->active)
dhcpcd_initstate1(ifp, argc, argv, 0);
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))) {
}
free(ctx.ifaces);
}
+ rt_dispose(&ctx);
free(ctx.duid);
if (ctx.link_fd != -1) {
eloop_event_delete(ctx.eloop, ctx.link_fd);
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);
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;
#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;
/*
- * dhcpcd - DHCP client daemon
+ * BSD interface driver for dhcpcd
* Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
* All rights reserved
# include <net80211/ieee80211_ioctl.h>
#endif
+#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include "ipv4ll.h"
#include "ipv6.h"
#include "ipv6nd.h"
+#include "route.h"
+#include "sa.h"
#include "bpf-filter.h"
#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
#endif
-#define COPYOUT(sin, sa) do { \
- if ((sa) && ((sa)->sa_family == AF_INET || (sa)->sa_family == 255)) \
- (sin) = ((const struct sockaddr_in *)(const void *) \
- (sa))->sin_addr; \
- } while (0)
-
-#define COPYOUT6(sin, sa) do { \
- if ((sa) && ((sa)->sa_family == AF_INET6 || (sa)->sa_family == 255)) \
- (sin) = ((const struct sockaddr_in6 *)(const void *) \
- (sa))->sin6_addr; \
- } while (0)
-
-#ifndef CLLADDR
-# define CLLADDR(s) (const void *)((s)->sdl_data + (s)->sdl_nlen)
+#ifdef INET6
+static void
+ifa_scope(struct sockaddr_in6 *, unsigned int);
#endif
struct priv {
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)); \
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
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
#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)
{
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,
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
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 ||
/*
- * dhcpcd - DHCP client daemon
+ * Linux interface driver for dhcpcd
* Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
* All rights reserved
#include "ipv4ll.h"
#include "ipv6.h"
#include "ipv6nd.h"
+#include "route.h"
+#include "sa.h"
#ifdef HAVE_NL80211_H
#include <linux/genetlink.h>
return r;
}
-#ifdef INET
static int
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)) {
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:
{
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);
}
}
+ 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
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;
}
len = nlm->nlmsg_len - sizeof(*nlm);
- if (len < sizeof(*rtm)) {
+ if (len < sizeof(struct rtmsg)) {
errno = EBADMSG;
return -1;
}
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;
}
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";
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)
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)
#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 */
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,
}
*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)
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
free(ifo->config[i++]);
free(ifo->config);
}
- ipv4_freeroutes(ifo->routes);
+ rt_headclear(&ifo->routes);
free(ifo->script);
free(ifo->arping);
free(ifo->blacklist);
#include <stdint.h>
#include "auth.h"
+#include "route.h"
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
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;
#include "ipv4.h"
#include "ipv6.h"
#include "ipv6nd.h"
+#include "route.h"
+#include "sa.h"
#ifndef ARP_MOD_NAME
# define ARP_MOD_NAME "arp"
(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
return NULL;
}
-#ifdef INET
static void
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)
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()) {
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;
/*
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
*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)
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";
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
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)
dhcp6_free(ifp);
ipv6nd_free(ifp);
ipv6_free(ifp);
+ rt_freeif(ifp);
free_options(ifp->options);
free(ifp);
}
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;
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)
{
#include <netinet/in_var.h> /* for IN_IFF_TENTATIVE et all */
#endif
-/* Some systems have route metrics.
- * OpenBSD route priority is not this. */
-#ifndef HAVE_ROUTE_METRIC
-# if defined(__linux__)
-# define HAVE_ROUTE_METRIC 1
-# endif
-#endif
-
-#if defined(__OpenBSD__) || defined (__sun)
-# define ROUTE_PER_GATEWAY
-/* XXX dhcpcd doesn't really support this yet.
- * But that's generally OK if only dhcpcd is managing routes. */
-#endif
-
/* Some systems have in-built IPv4 DAD.
* However, we need them to do DAD at carrier up as well. */
#ifdef IN_IFF_TENTATIVE
#include "dhcpcd.h"
#include "ipv4.h"
#include "ipv6.h"
+#include "route.h"
#define EUI64_ADDR_LEN 8
#define INFINIBAND_ADDR_LEN 20
#define RAW_EOF 1 << 0
#define RAW_PARTIALCSUM 2 << 0
+#ifndef CLLADDR
+#ifdef AF_LINK
+# define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
+#endif
+#endif
+
#ifdef __sun
/* Solaris stupidly defines this for compat with BSD
* but then ignores it. */
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);
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
#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))
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)
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;
if (cp3 == cplim)
break;
}
- if (rtn != rtp)
+ if (rth != rt)
continue;
if ((state = D_CSTATE(ifp)) == NULL)
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: 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
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;
}
out:
- ipv4_buildroutes(ifp->ctx);
+ rt_build(ifp->ctx, AF_INET);
return preferred;
}
{
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;
}
* 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 */
TAILQ_REMOVE(&state->addrs, ia, next);
free(ia);
}
- ipv4_freerts(&state->routes);
#ifdef BSD
free(state->buffer);
#endif
}
}
}
-
-void
-ipv4_ctxfree(struct dhcpcd_ctx *ctx)
-{
-
- ipv4_freeroutes(ctx->ipv4_routes);
- ipv4_freeroutes(ctx->ipv4_kroutes);
-}
(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;
struct ipv4_state {
struct ipv4_addrhead addrs;
- struct rt_head routes;
#ifdef BSD
/* Buffer for BPF */
#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 *);
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
#include <assert.h>
#include <errno.h>
#include <signal.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "if-options.h"
#include "ipv4.h"
#include "ipv4ll.h"
+#include "sa.h"
#include "script.h"
#ifdef IPV4LL
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
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);
ifp->if_data[IF_DATA_IPV4LL] = NULL;
if (dropped) {
- ipv4_buildroutes(ifp->ctx);
+ rt_build(ifp->ctx, AF_INET);
script_runreason(ifp, "IPV4LL");
}
}
* 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;
}
}
(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);
#include "eloop.h"
#include "ipv6.h"
#include "ipv6nd.h"
+#include "sa.h"
#include "script.h"
#ifdef HAVE_MD5_H
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;
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;
}
/* Load existing routes */
- if_initrt6(ifp->ctx);
+ if_initrt(ifp->ctx);
return 0;
}
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,
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);
}
}
#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;
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;
}
#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 *);
int nd_fd;
struct ra_head *ra_routers;
- struct rt6_head *routes;
-
- struct rt6_head kroutes;
int dhcp_fd;
};
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)
#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 */
if (seconds)
ipv6nd_expirera(ifp);
else
- ipv6_buildroutes(ifp->ctx);
+ rt_build(ifp->ctx, AF_INET6);
}
static void
"%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");
}
"%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");
}
/* 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;
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");
}
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");
}
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;
}
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 */
}
}
--- /dev/null
+/*
+ * dhcpcd - route management
+ * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcpcd.h"
+#include "if.h"
+#include "ipv4.h"
+#include "ipv4ll.h"
+#include "ipv6.h"
+#include "route.h"
+#include "sa.h"
+
+void
+rt_init(struct dhcpcd_ctx *ctx)
+{
+
+ TAILQ_INIT(&ctx->routes);
+ TAILQ_INIT(&ctx->kroutes);
+ TAILQ_INIT(&ctx->froutes);
+}
+
+static void
+rt_desc(const char *cmd, const struct rt *rt)
+{
+ char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
+ int prefix;
+ struct dhcpcd_ctx *ctx;
+ const char *ifname;
+ bool gateway_unspec;
+
+ assert(cmd != NULL);
+ assert(rt != NULL);
+ assert(rt->rt_ifp != NULL);
+
+ ctx = rt->rt_ifp->ctx;
+ ifname = rt->rt_ifp->name;
+ sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
+ prefix = sa_toprefix(&rt->rt_netmask);
+ sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
+
+ gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
+
+ if (rt->rt_flags & RTF_HOST) {
+ if (gateway_unspec)
+ logger(ctx, LOG_INFO, "%s: %s host route to %s",
+ ifname, cmd, dest);
+ else
+ logger(ctx, LOG_INFO, "%s: %s host route to %s via %s",
+ ifname, cmd, dest, gateway);
+ } else if (sa_is_unspecified(&rt->rt_dest) &&
+ sa_is_unspecified(&rt->rt_netmask))
+ {
+ if (gateway_unspec)
+ logger(ctx, LOG_INFO, "%s: %s default route",
+ ifname, cmd);
+ else
+ logger(ctx, LOG_INFO, "%s: %s default route via %s",
+ ifname, cmd, gateway);
+ } else if (gateway_unspec)
+ logger(ctx, LOG_INFO, "%s: %s route to %s/%d",
+ ifname, cmd, dest, prefix);
+ else
+ logger(ctx, LOG_INFO, "%s: %s route to %s/%d via %s",
+ ifname, cmd, dest, prefix, gateway);
+}
+
+void
+rt_headclear(struct rt_head *rts)
+{
+ struct rt *rt;
+ struct dhcpcd_ctx *ctx;
+
+ if (rts == NULL)
+ return;
+
+ ctx = NULL;
+ while ((rt = TAILQ_FIRST(rts))) {
+ TAILQ_REMOVE(rts, rt, rt_next);
+ if (ctx == NULL) {
+ ctx = rt->rt_ifp->ctx;
+ assert(&ctx->froutes != rts);
+ }
+ TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next);
+ }
+}
+
+static void
+rt_headfree(struct rt_head *rts)
+{
+ struct rt *rt;
+
+ while ((rt = TAILQ_FIRST(rts))) {
+ TAILQ_REMOVE(rts, rt, rt_next);
+ free(rt);
+ }
+}
+
+void
+rt_dispose(struct dhcpcd_ctx *ctx)
+{
+
+ assert(ctx != NULL);
+ rt_headfree(&ctx->routes);
+ rt_headfree(&ctx->kroutes);
+ rt_headfree(&ctx->froutes);
+}
+
+struct rt *
+rt_new(struct interface *ifp)
+{
+ struct rt *rt;
+ struct dhcpcd_ctx *ctx;
+
+ assert(ifp != NULL);
+ ctx = ifp->ctx;
+ if ((rt = TAILQ_FIRST(&ctx->froutes)) != NULL)
+ TAILQ_REMOVE(&ctx->froutes, rt, rt_next);
+ else if ((rt = malloc(sizeof(*rt))) == NULL) {
+ logger(ctx, LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+ memset(rt, 0, sizeof(*rt));
+ rt->rt_ifp = ifp;
+#ifdef HAVE_ROUTE_METRIC
+ rt->rt_metric = ifp->metric;
+#endif
+ return rt;
+}
+
+void
+rt_free(struct rt *rt)
+{
+
+ assert(rt != NULL);
+ assert(rt->rt_ifp->ctx != NULL);
+ TAILQ_INSERT_TAIL(&rt->rt_ifp->ctx->froutes, rt, rt_next);
+}
+
+void
+rt_freeif(struct interface *ifp)
+{
+ struct dhcpcd_ctx *ctx;
+ struct rt *rt, *rtn;
+
+ if (ifp == NULL)
+ return;
+ ctx = ifp->ctx;
+ TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) {
+ if (rt->rt_ifp == ifp) {
+ TAILQ_REMOVE(&ctx->routes, rt, rt_next);
+ rt_free(rt);
+ }
+ }
+ TAILQ_FOREACH_SAFE(rt, &ctx->kroutes, rt_next, rtn) {
+ if (rt->rt_ifp == ifp) {
+ TAILQ_REMOVE(&ctx->kroutes, rt, rt_next);
+ rt_free(rt);
+ }
+ }
+}
+
+struct rt *
+rt_find(struct rt_head *rts, const struct rt *f)
+{
+ struct rt *rt;
+
+ assert(rts != NULL);
+ assert(f != NULL);
+ TAILQ_FOREACH(rt, rts, rt_next) {
+ if (sa_cmp(&rt->rt_dest, &f->rt_dest) == 0 &&
+#ifdef HAVE_ROUTE_METRIC
+ rt->rt_ifp->metric == f->rt_ifp->metric &&
+#endif
+ sa_cmp(&rt->rt_netmask, &f->rt_netmask) == 0)
+ return rt;
+ }
+ return NULL;
+}
+
+static void
+rt_kfree(struct rt *rt)
+{
+ struct dhcpcd_ctx *ctx;
+ struct rt *f;
+
+ assert(rt != NULL);
+ ctx = rt->rt_ifp->ctx;
+ if ((f = rt_find(&ctx->kroutes, rt)) != NULL) {
+ TAILQ_REMOVE(&ctx->kroutes, f, rt_next);
+ rt_free(f);
+ }
+}
+
+/* If something other than dhcpcd removes a route,
+ * we need to remove it from our internal table. */
+void
+rt_recvrt(int cmd, const struct rt *rt)
+{
+ struct dhcpcd_ctx *ctx;
+ struct rt *f;
+
+ assert(rt != NULL);
+ ctx = rt->rt_ifp->ctx;
+ f = rt_find(&ctx->kroutes, rt);
+
+ switch(cmd) {
+ case RTM_DELETE:
+ if (f != NULL) {
+ TAILQ_REMOVE(&ctx->kroutes, f, rt_next);
+ rt_free(f);
+ }
+ if ((f = rt_find(&ctx->routes, rt)) != NULL) {
+ TAILQ_REMOVE(&ctx->routes, f, rt_next);
+ rt_desc("deleted", f);
+ rt_free(f);
+ }
+ break;
+ case RTM_ADD:
+ if (f != NULL)
+ break;
+ if ((f = rt_new(rt->rt_ifp)) == NULL)
+ break;
+ memcpy(f, rt, sizeof(*f));
+ TAILQ_INSERT_TAIL(&ctx->kroutes, f, rt_next);
+ break;
+ }
+
+#ifdef HAVE_ROUTE_METRIC
+ if (rt->rt_dest.sa_family == AF_INET)
+ ipv4ll_recvrt(cmd, rt);
+#endif
+}
+
+static bool
+rt_add(struct rt *nrt, struct rt *ort)
+{
+ struct dhcpcd_ctx *ctx;
+ bool change;
+
+ assert(nrt != NULL);
+ ctx = nrt->rt_ifp->ctx;
+
+ /* Don't set default routes if not asked to */
+ if (!(nrt->rt_ifp->options->options & DHCPCD_GATEWAY) &&
+ sa_is_unspecified(&nrt->rt_dest) &&
+ sa_is_unspecified(&nrt->rt_netmask))
+ return false;
+
+ rt_desc(ort == NULL ? "adding" : "changing", nrt);
+
+ change = false;
+ if (ort == NULL) {
+ ort = rt_find(&ctx->kroutes, nrt);
+ if (ort != NULL &&
+ ((ort->rt_flags & RTF_REJECT &&
+ nrt->rt_flags & RTF_REJECT) ||
+ (ort->rt_ifp == nrt->rt_ifp &&
+#ifdef HAVE_ROUTE_METRIC
+ ort->rt_metric == nrt->rt_metric &&
+#endif
+ sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
+ {
+ if (ort->rt_mtu == nrt->rt_mtu)
+ return true;
+ change = true;
+ }
+ } else if (ort->rt_dflags & RTDF_FAKE &&
+ !(nrt->rt_dflags & RTDF_FAKE) &&
+ ort->rt_ifp == nrt->rt_ifp &&
+#ifdef HAVE_ROUTE_METRIC
+ ort->rt_metric == nrt->rt_metric &&
+#endif
+ sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
+ sa_cmp(&ort->rt_netmask, &nrt->rt_netmask) == 0 &&
+ sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
+ {
+ if (ort->rt_mtu == nrt->rt_mtu)
+ return 0;
+ change = true;
+ }
+
+#ifdef RTF_CLONING
+ /* BSD can set routes to be cloning routes.
+ * Cloned routes inherit the parent flags.
+ * As such, we need to delete and re-add the route to flush children
+ * to correct the flags. */
+ if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
+ change = true;
+#endif
+
+ if (change) {
+ if (if_route(RTM_CHANGE, nrt) != -1)
+ return true;
+ if (errno != ESRCH)
+ logger(ctx, LOG_ERR, "if_route (CHG): %m");
+ }
+
+#ifdef HAVE_ROUTE_METRIC
+ /* With route metrics, we can safely add the new route before
+ * deleting the old route. */
+ if (if_route(RTM_ADD, nrt) != -1) {
+ if (ort != NULL) {
+ if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
+ logger(ctx, LOG_ERR, "if_route (DEL): %m");
+ rt_kfree(ort);
+ }
+ return true;
+ }
+
+ /* If the kernel claims the route exists we need to rip out the
+ * old one first. */
+ if (errno != EEXIST || ort == NULL)
+ goto logerr;
+#endif
+
+ /* No route metrics, we need to delete the old route before
+ * adding the new one. */
+#ifdef ROUTE_PER_GATEWAY
+ errno = 0;
+#endif
+ if (ort != NULL) {
+ if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
+ logger(ctx, LOG_ERR, "if_route (DEL): %m");
+ else
+ rt_kfree(ort);
+ }
+#ifdef ROUTE_PER_GATEWAY
+ /* The OS allows many routes to the same dest with different gateways.
+ * dhcpcd does not support this yet, so for the time being just keep on
+ * deleting the route until there is an error. */
+ if (ort != NULL && errno == 0) {
+ for (;;) {
+ if (if_route(RTM_DELETE, ort) == -1)
+ break;
+ }
+ }
+#endif
+ if (if_route(RTM_ADD, nrt) != -1)
+ return true;
+#ifdef HAVE_ROUTE_METRIC
+logerr:
+#endif
+ logger(ctx, LOG_ERR, "if_route (ADD): %m");
+ return false;
+}
+
+static bool
+rt_delete(struct rt *rt)
+{
+ int retval;
+
+ rt_desc("deleting", rt);
+ retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
+ if (!retval && errno != ENOENT && errno != ESRCH)
+ logger(rt->rt_ifp->ctx, LOG_ERR,
+ "%s: if_delroute: %m", rt->rt_ifp->name);
+ /* Remove the route from our kernel table so we can add a
+ * IPv4LL default route if possible. */
+ else
+ rt_kfree(rt);
+ return retval;
+}
+
+static bool
+rt_cmp(const struct rt *r1, const struct rt *r2)
+{
+
+ return (r1->rt_ifp == r2->rt_ifp &&
+#ifdef HAVE_ROUTE_METRIC
+ r1->rt_metric == r2->rt_metric &&
+#endif
+ sa_cmp(&r1->rt_gateway, &r1->rt_gateway) == 0);
+}
+
+static bool
+rt_doroute(struct rt *rt)
+{
+ struct dhcpcd_ctx *ctx;
+ struct rt *or;
+
+ ctx = rt->rt_ifp->ctx;
+ /* Do we already manage it? */
+ if ((or = rt_find(&ctx->routes, rt))) {
+ if (rt->rt_dflags & RTDF_FAKE)
+ return true;
+ if (or->rt_dflags & RTDF_FAKE ||
+ !rt_cmp(rt,or) ||
+ (rt->rt_ifa.sa_family != AF_UNSPEC &&
+ sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
+ or->rt_mtu != rt->rt_mtu)
+ {
+ if (!rt_add(rt, or))
+ return false;
+ }
+ TAILQ_REMOVE(&ctx->routes, or, rt_next);
+ rt_free(or);
+ } else {
+ if (rt->rt_dflags & RTDF_FAKE) {
+ if ((or = rt_find(&ctx->kroutes, rt)) == NULL)
+ return false;
+ if (!rt_cmp(rt, or))
+ return false;
+ } else {
+ if (!rt_add(rt, NULL))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+rt_build(struct dhcpcd_ctx *ctx, int af)
+{
+ struct rt_head routes, added;
+ struct rt *rt, *rtn;
+ unsigned long long o;
+#ifdef INET6
+ bool have_default = false;
+#endif
+
+ /* We need to have the interfaces in the correct order to ensure
+ * our routes are managed correctly. */
+ if_sortinterfaces(ctx);
+
+ TAILQ_INIT(&routes);
+ TAILQ_INIT(&added);
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ if (!inet_getroutes(ctx, &routes))
+ return;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ if (!inet6_getroutes(ctx, &routes))
+ return;
+ break;
+#endif
+ }
+
+ TAILQ_FOREACH_SAFE(rt, &routes, rt_next, rtn) {
+ if (rt->rt_dest.sa_family != af &&
+ rt->rt_gateway.sa_family != af)
+ continue;
+ /* Is this route already in our table? */
+ if ((rt_find(&added, rt)) != NULL)
+ continue;
+ if (rt_doroute(rt)) {
+ TAILQ_REMOVE(&routes, rt, rt_next);
+ TAILQ_INSERT_TAIL(&added, rt, rt_next);
+#ifdef INET6
+ if (sa_is_unspecified(&rt->rt_dest) &&
+ sa_is_unspecified(&rt->rt_netmask))
+ have_default = true;
+#endif
+ }
+ }
+
+ /* Remove old routes we used to manage
+ * If we own the default route, but not RA management itself
+ * then we need to preserve the last best default route we had */
+ TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) {
+ if (rt->rt_dest.sa_family != af &&
+ rt->rt_gateway.sa_family != af)
+ continue;
+ TAILQ_REMOVE(&ctx->routes, rt, rt_next);
+ if (rt_find(&added, rt) == NULL) {
+ o = rt->rt_ifp->options ?
+ rt->rt_ifp->options->options :
+ ctx->options;
+#ifdef INET6
+ if (!have_default &&
+ (o & DHCPCD_IPV6RA_OWN_DEFAULT) &&
+ !(o & DHCPCD_IPV6RA_OWN) &&
+ rt->rt_dest.sa_family == AF_INET6 &&
+ sa_is_unspecified(&rt->rt_dest))
+ have_default = true;
+ /* no need to add it back to our routing table
+ * as we delete an exiting route when we add
+ * a new one */
+ else
+#endif
+ if ((o &
+ (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
+ (DHCPCD_EXITING | DHCPCD_PERSISTENT))
+ rt_delete(rt);
+ }
+ TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next);
+ }
+
+ rt_headclear(&ctx->routes);
+ TAILQ_CONCAT(&ctx->routes, &added, rt_next);
+ rt_headclear(&routes);
+}
--- /dev/null
+/*
+ * dhcpcd - route management
+ * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * rEDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ROUTE_H
+#define ROUTE_H
+
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <stdbool.h>
+
+#include "dhcpcd.h"
+
+/* Some systems have route metrics.
+ * OpenBSD route priority is not this. */
+#ifndef HAVE_ROUTE_METRIC
+# if defined(__linux__)
+# define HAVE_ROUTE_METRIC 1
+# endif
+#endif
+
+#if defined(__OpenBSD__) || defined (__sun)
+# define ROUTE_PER_GATEWAY
+/* XXX dhcpcd doesn't really support this yet.
+ * But that's generally OK if only dhcpcd is managing routes. */
+#endif
+
+union sa_ss {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+};
+
+struct rt {
+ TAILQ_ENTRY(rt) rt_next;
+ union sa_ss rt_ss_dest;
+#define rt_dest rt_ss_dest.sa
+ union sa_ss rt_ss_netmask;
+#define rt_netmask rt_ss_netmask.sa
+ union sa_ss rt_ss_gateway;
+#define rt_gateway rt_ss_gateway.sa
+ struct interface *rt_ifp;
+ union sa_ss rt_ss_ifa;
+#define rt_ifa rt_ss_ifa.sa
+ unsigned int rt_flags;
+ unsigned int rt_mtu;
+#ifdef HAVE_ROUTE_METRIC
+ unsigned int rt_metric;
+#endif
+ unsigned int rt_dflags;
+#define RTDF_INIT 0x01 /* Generated by if_initrt() */
+#define RTDF_IFA_ROUTE 0x02 /* Address generated route */
+#define RTDF_FAKE 0x04 /* Maybe us on lease reboot */
+};
+TAILQ_HEAD(rt_head, rt);
+
+void rt_init(struct dhcpcd_ctx *);
+void rt_dispose(struct dhcpcd_ctx *);
+struct rt * rt_find(struct rt_head *, const struct rt *);
+void rt_free(struct rt *);
+void rt_freeif(struct interface *);
+void rt_headclear(struct rt_head *);
+void rt_headfreeif(struct rt_head *);
+struct rt * rt_new(struct interface *);
+void rt_recvrt(int, const struct rt *);
+void rt_build(struct dhcpcd_ctx *, int);
+
+#endif
--- /dev/null
+/*
+ * Socket Address handling for dhcpcd
+ * Copyright (c) 2015-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#ifdef AF_LINK
+#include <net/if_dl.h>
+#elif AF_PACKET
+#include <linux/if_packet.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include "common.h"
+#include "sa.h"
+
+socklen_t
+sa_addroffset(const struct sockaddr *sa)
+{
+
+ assert(sa != NULL);
+ switch(sa->sa_family) {
+#ifdef INET
+ case AF_INET:
+ return offsetof(struct sockaddr_in, sin_addr) +
+ offsetof(struct in_addr, s_addr);
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ return offsetof(struct sockaddr_in6, sin6_addr) +
+ offsetof(struct in6_addr, s6_addr);
+#endif /* INET6 */
+ default:
+ errno = EAFNOSUPPORT;
+ return 0;
+ }
+}
+
+socklen_t
+sa_addrlen(const struct sockaddr *sa)
+{
+#define membersize(type, member) sizeof(((type *)0)->member)
+ assert(sa != NULL);
+ switch(sa->sa_family) {
+#ifdef INET
+ case AF_INET:
+ return membersize(struct in_addr, s_addr);
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ return membersize(struct in6_addr, s6_addr);
+#endif /* INET6 */
+ default:
+ errno = EAFNOSUPPORT;
+ return 0;
+ }
+}
+
+bool
+sa_is_unspecified(const struct sockaddr *sa)
+{
+
+ assert(sa != NULL);
+ switch(sa->sa_family) {
+ case AF_UNSPEC:
+ return true;
+#ifdef INET
+ case AF_INET:
+ return satocsin(sa)->sin_addr.s_addr == INADDR_ANY;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ return IN6_IS_ADDR_UNSPECIFIED(&satocsin6(sa)->sin6_addr);
+#endif /* INET6 */
+ default:
+ errno = EAFNOSUPPORT;
+ return false;
+ }
+}
+
+#ifdef INET6
+#ifndef IN6MASK128
+#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
+#endif
+static const struct in6_addr in6allones = IN6MASK128;
+#endif
+
+bool
+sa_is_allones(const struct sockaddr *sa)
+{
+
+ assert(sa != NULL);
+ switch(sa->sa_family) {
+ case AF_UNSPEC:
+ return false;
+#ifdef INET
+ case AF_INET:
+ {
+ const struct sockaddr_in *sin;
+
+ sin = satocsin(sa);
+ return sin->sin_addr.s_addr == INADDR_BROADCAST;
+ }
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *sin6;
+
+ sin6 = satocsin6(sa);
+ return IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &in6allones);
+ }
+#endif /* INET6 */
+ default:
+ errno = EAFNOSUPPORT;
+ return false;
+ }
+}
+
+bool
+sa_is_loopback(const struct sockaddr *sa)
+{
+
+ assert(sa != NULL);
+ switch(sa->sa_family) {
+ case AF_UNSPEC:
+ return false;
+#ifdef INET
+ case AF_INET:
+ {
+ const struct sockaddr_in *sin;
+
+ sin = satocsin(sa);
+ return sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK);
+ }
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *sin6;
+
+ sin6 = satocsin6(sa);
+ return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);
+ }
+#endif /* INET6 */
+ default:
+ errno = EAFNOSUPPORT;
+ return false;
+ }
+}
+
+int
+sa_toprefix(const struct sockaddr *sa)
+{
+
+ assert(sa != NULL);
+ switch(sa->sa_family) {
+#ifdef INET
+ case AF_INET:
+ {
+ const struct sockaddr_in *sin;
+ int mask;
+ int cidr;
+
+ sin = satocsin(sa);
+ mask = (int)ntohl(sin->sin_addr.s_addr);
+ cidr = 33 - ffs(mask); /* 33 - (1 .. 32) -> 32 .. 1 */
+ if (cidr < 32) { /* more than 1 bit in mask */
+ /* check for non-contig netmask */
+ if ((mask ^ (((1 << cidr) - 1) << (32 - cidr))) != 0) {
+ errno = EINVAL;
+ return -1; /* noncontig, no pfxlen */
+ }
+ }
+ return cidr;
+ }
+#endif
+#ifdef INET6
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *sin6;
+ int x, y;
+ const uint8_t *lim, *p;
+
+ sin6 = satocsin6(sa);
+ p = (const uint8_t *)sin6->sin6_addr.s6_addr;
+ lim = p + sizeof(sin6->sin6_addr.s6_addr);
+ for (x = 0; p < lim; x++, p++) {
+ if (*p != 0xff)
+ break;
+ }
+ y = 0;
+ if (p < lim) {
+ for (y = 0; y < NBBY; y++) {
+ if ((*p & (0x80 >> y)) == 0)
+ break;
+ }
+ }
+
+ /*
+ * when the limit pointer is given, do a stricter check on the
+ * remaining bits.
+ */
+ if (p < lim) {
+ if (y != 0 && (*p & (0x00ff >> y)) != 0)
+ return 0;
+ for (p = p + 1; p < lim; p++)
+ if (*p != 0)
+ return 0;
+ }
+
+ return (uint8_t)(x * NBBY + y);
+ }
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+}
+
+int
+sa_fromprefix(struct sockaddr *sa, int prefix)
+{
+ uint8_t *ap, a;
+ int max_prefix, i;
+
+ switch (sa->sa_family) {
+#ifdef INET
+ case AF_INET:
+ max_prefix = 32;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ max_prefix = 128;
+ break;
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ ap = (uint8_t *)sa + sa_addroffset(sa);
+ for (i = 0; i < (prefix / NBBY); i++)
+ *ap++ = 0xff;
+ a = 0xff;
+ a = (uint8_t)(a << (8 - (prefix % NBBY)));
+ *ap = a;
+ for (i = 0; i < ((max_prefix - prefix) / NBBY); i++)
+ *ap++ = 0x00;
+ return 0;
+}
+
+/* inet_ntop, but for sockaddr. */
+const char *
+sa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len)
+{
+ const void *addr;
+
+ assert(buf != NULL);
+#ifdef AF_LINK
+#ifndef CLLADDR
+#define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
+#endif
+ if (sa->sa_family == AF_LINK) {
+ const struct sockaddr_dl *sdl;
+
+ sdl = (const void *)sa;
+ if (sdl->sdl_alen == 0) {
+ if (snprintf(buf, len, "link#%d", sdl->sdl_index) == -1)
+ return NULL;
+ return buf;
+ }
+ return hwaddr_ntoa(CLLADDR(sdl), sdl->sdl_alen, buf, len);
+ }
+#elif AF_PACKET
+ if (sa->sa_family == AF_PACKET) {
+ const struct sockaddr_ll *sll;
+
+ sll = (const void *)sa;
+ return hwaddr_ntoa(sll->sll_addr, sll->sll_halen, buf, len);
+ }
+#endif
+ addr = (const char *)sa + sa_addroffset(sa);
+ return inet_ntop(sa->sa_family, addr, buf, len);
+}
+
+int
+sa_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+ socklen_t offset, len;
+
+ assert(sa1 != NULL);
+ assert(sa2 != NULL);
+
+ /* Treat AF_UNSPEC as the unspecified address. */
+ if ((sa1->sa_family == AF_UNSPEC || sa2->sa_family == AF_UNSPEC) &&
+ sa_is_unspecified(sa1) && sa_is_unspecified(sa2))
+ return 0;
+
+ if (sa1->sa_family != sa2->sa_family)
+ return sa1->sa_family - sa2->sa_family;
+
+#ifdef HAVE_SA_LEN
+ len = MIN(sa1->sa_len, sa2->sa_len);
+#endif
+
+ switch (sa1->sa_family) {
+#ifdef INET
+ case AF_INET:
+ offset = offsetof(struct sockaddr_in, sin_addr);
+#ifdef HAVE_SA_LEN
+ len -= offset;
+#else
+ len = sizeof(struct in_addr);
+#endif
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ offset = offsetof(struct sockaddr_in6, sin6_addr);
+#ifdef HAVE_SA_LEN
+ len -= offset;
+#else
+ len = sizeof(struct in6_addr);
+#endif
+ break;
+#endif
+ default:
+ offset = 0;
+#ifndef HAVE_SA_LEN
+ len = sizeof(struct sockaddr);
+#endif
+ break;
+ }
+
+ return memcmp((const char *)sa1 + offset,
+ (const char *)sa2 + offset,
+ len);
+}
+
+#ifdef INET
+void
+sa_in_init(struct sockaddr *sa, const struct in_addr *addr)
+{
+ struct sockaddr_in *sin;
+
+ assert(sa != NULL);
+ assert(addr != NULL);
+ sin = satosin(sa);
+ sin->sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sin->sin_len = sizeof(*sin);
+#endif
+ sin->sin_addr.s_addr = addr->s_addr;
+}
+#endif
+
+#ifdef INET6
+void
+sa_in6_init(struct sockaddr *sa, const struct in6_addr *addr)
+{
+ struct sockaddr_in6 *sin6;
+
+ assert(sa != NULL);
+ assert(addr != NULL);
+ sin6 = satosin6(sa);
+ sin6->sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ sin6->sin6_len = sizeof(*sin6);
+#endif
+ memcpy(&sin6->sin6_addr.s6_addr, &addr->s6_addr,
+ sizeof(sin6->sin6_addr.s6_addr));
+}
+#endif
--- /dev/null
+/*
+ * Socket Address handling for dhcpcd
+ * Copyright (c) 2015-2016 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SA_H
+#define SA_H
+
+#include <sys/socket.h>
+
+#ifndef __linux__
+#define HAVE_SA_LEN
+#endif
+
+/* Allow for a sockaddr_dl being printed too. */
+#define INET_MAX_ADDRSTRLEN (20 * 3)
+
+#ifdef INET
+#define satosin(sa) ((struct sockaddr_in *)(void *)(sa))
+#define satocsin(sa) ((const struct sockaddr_in *)(const void *)(sa))
+#endif
+#ifdef INET6
+#define satosin6(sa) ((struct sockaddr_in6 *)(void *)(sa))
+#define satocsin6(sa) ((const struct sockaddr_in6 *)(const void *)(sa))
+#endif
+
+socklen_t sa_addroffset(const struct sockaddr *sa);
+socklen_t sa_addrlen(const struct sockaddr *sa);
+bool sa_is_unspecified(const struct sockaddr *);
+bool sa_is_allones(const struct sockaddr *);
+bool sa_is_loopback(const struct sockaddr *);
+void *sa_toaddr(struct sockaddr *);
+int sa_toprefix(const struct sockaddr *);
+int sa_fromprefix(struct sockaddr *, int);
+const char *sa_addrtop(const struct sockaddr *, char *, socklen_t);
+int sa_cmp(const struct sockaddr *, const struct sockaddr *);
+void sa_in_init(struct sockaddr *, const struct in_addr *);
+void sa_in6_init(struct sockaddr *, const struct in6_addr *);
+
+#endif