}
}
+ rt_discover(&ctx);
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
if (ifp->active)
dhcpcd_initstate1(ifp, argc, argv, 0);
size_t ctl_extra;
rb_tree_t routes; /* our routes */
+ rb_tree_t kroutes; /* kernel routes */
#ifdef RT_FREE_ROUTE_TABLE
rb_tree_t froutes; /* free routes for re-use */
#endif
return true;
}
-static int
-if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
+static struct rt *
+if_rtmtort(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
{
const struct sockaddr *rti_info[RTAX_MAX];
+ struct interface *ifp;
+ struct rt *rt;
if (!(rtm->rtm_addrs & RTA_DST)) {
errno = EINVAL;
- return -1;
+ return NULL;
}
if (rtm->rtm_type != RTM_MISS && !(rtm->rtm_addrs & RTA_GATEWAY)) {
errno = EINVAL;
- return -1;
+ return NULL;
}
if (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),
rtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)
- return -1;
- memset(rt, 0, sizeof(*rt));
+ return NULL;
+
+ if (rtm->rtm_index)
+ ifp = if_findindex(ctx->ifaces, rtm->rtm_index);
+ else if (rtm->rtm_addrs & RTA_IFP)
+ ifp = if_findsa(ctx, rti_info[RTAX_IFP]);
+ else if (rtm->rtm_addrs & RTA_GATEWAY)
+ ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);
+ else
+ ifp = if_findsa(ctx, rti_info[RTAX_DST]);
+
+ if (ifp == NULL && rtm->rtm_type == RTM_MISS)
+ ifp = if_find(ctx->ifaces, "lo0");
+
+ if (ifp == NULL) {
+ errno = ESRCH;
+ return NULL;
+ }
+
+ rt = rt_new0(ctx);
+ rt->rt_ifp = ifp;
rt->rt_flags = (unsigned int)rtm->rtm_flags;
if_copysa(&rt->rt_dest, rti_info[RTAX_DST]);
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 && rtm->rtm_type == RTM_MISS)
- rt->rt_ifp = if_find(ctx->ifaces, "lo0");
-
- if (rt->rt_ifp == NULL) {
- errno = ESRCH;
- return -1;
- }
- return 0;
+ return rt;
}
static int
}
int
-if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
+if_initrt(struct dhcpcd_ctx *ctx, int af)
{
struct rt_msghdr *rtm;
int mib[6] = { CTL_NET, PF_ROUTE, 0, af, NET_RT_DUMP, 0 };
size_t bufl;
char *buf = NULL, *p, *end;
- struct rt rt, *rtn;
+ struct rt *rt;
again:
if (if_sysctl(ctx, mib, __arraycount(mib), NULL, &bufl, NULL, 0) == -1)
}
if (!if_realroute(rtm))
continue;
- if (if_copyrt(ctx, &rt, rtm) != 0)
+ if ((rt = if_rtmtort(ctx, rtm)) == NULL)
continue;
- if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
- logerr(__func__);
- break;
- }
- memcpy(rtn, &rt, sizeof(*rtn));
- if (rb_tree_insert_node(kroutes, rtn) != rtn)
- rt_free(rtn);
+ if (rb_tree_insert_node(&ctx->kroutes, rt) != rt)
+ rt_free(rt);
}
free(buf);
return p == end ? 0 : -1;
static int
if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
{
- struct rt rt;
+ struct rt *rt;
if (rtm->rtm_msglen < sizeof(*rtm)) {
errno = EINVAL;
}
#endif
- if (if_copyrt(ctx, &rt, rtm) == -1)
+ if ((rt = if_rtmtort(ctx, rtm)) == NULL)
return errno == ENOTSUP ? 0 : -1;
#ifdef INET6
* existance with a hardware address.
* Ensure we don't call this for a newly incomplete state.
*/
- if (rt.rt_dest.sa_family == AF_INET6 &&
- (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
- !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK)))
+ if (rt->rt_dest.sa_family == AF_INET6 &&
+ (rt->rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&
+ !(rtm->rtm_type == RTM_ADD && !(rt->rt_dflags & RTDF_GATELINK)))
{
bool reachable;
reachable = (rtm->rtm_type == RTM_ADD ||
rtm->rtm_type == RTM_CHANGE) &&
- rt.rt_dflags & RTDF_GATELINK;
- ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);
+ rt->rt_dflags & RTDF_GATELINK;
+ ipv6nd_neighbour(ctx, &rt->rt_ss_dest.sin6.sin6_addr, reachable);
}
#endif
if (rtm->rtm_type != RTM_MISS && if_realroute(rtm))
- rt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);
+ rt_recvrt(rtm->rtm_type, rt, rtm->rtm_pid);
return 0;
}
int xsocketpair(int, int, int, int[2]);
int if_route(unsigned char, const struct rt *rt);
-int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
+int if_initrt(struct dhcpcd_ctx *, int);
int if_missfilter(struct interface *, struct sockaddr *);
int if_missfilter_apply(struct dhcpcd_ctx *);
};
#endif
+void
+rt_discover(struct dhcpcd_ctx *ctx)
+{
+
+ rt_headclear(&ctx->kroutes, AF_UNSPEC);
+ if (if_initrt(ctx, AF_UNSPEC) != 0)
+ logerr("%s: if_initrt", __func__);
+}
+
void
rt_init(struct dhcpcd_ctx *ctx)
{
rb_tree_init(&ctx->routes, &rt_compare_os_ops);
+ rb_tree_init(&ctx->kroutes, &rt_compare_os_ops);
#ifdef RT_FREE_ROUTE_TABLE
rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
#endif
assert(ctx != NULL);
rt_headfree(&ctx->routes);
+ rt_headfree(&ctx->kroutes);
#ifdef RT_FREE_ROUTE_TABLE
rt_headfree(&ctx->froutes);
#ifdef RT_FREE_ROUTE_TABLE_STATS
#endif
} else
#endif
- if ((rt = malloc(sizeof(*rt))) == NULL) {
+ if (rt == NULL && (rt = malloc(sizeof(*rt))) == NULL)
logerr(__func__);
- return NULL;
- }
- memset(rt, 0, sizeof(*rt));
+ else
+ memset(rt, 0, sizeof(*rt));
return rt;
}
/* 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, pid_t pid)
+rt_recvrt(int cmd, struct rt *rt, pid_t pid)
{
struct dhcpcd_ctx *ctx;
struct rt *f;
+ bool freert = false;
assert(rt != NULL);
assert(rt->rt_ifp != NULL);
ctx = rt->rt_ifp->ctx;
switch(cmd) {
+ case RTM_ADD: /* FALLTHROUGH */
+ case RTM_CHANGE: /* FALLTHROUGH */
+ case RTM_DELETE:
+ /* For any of these actions, we delete any matching
+ * kernel route we have. */
+ f = rb_tree_find_node(&ctx->kroutes, rt);
+ if (f != NULL) {
+ rb_tree_remove_node(&ctx->kroutes, f);
+ rt_free(f);
+ }
+ break;
+ }
+
+ switch(cmd) {
+ case RTM_ADD: /* FALLTHROUGH */
+ case RTM_CHANGE:
+ if (rb_tree_insert_node(&ctx->kroutes, rt) != rt) /* safety */
+ freert = true;
+ break;
case RTM_DELETE:
f = rb_tree_find_node(&ctx->routes, rt);
if (f != NULL) {
if (rt->rt_dest.sa_family == AF_INET)
ipv4ll_recvrt(cmd, rt);
#endif
+
+ if (freert)
+ rt_free(rt);
}
/* Compare miscellaneous route details */
#endif
static bool
-rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
+rt_add(struct rt *nrt, struct rt *ort)
{
struct dhcpcd_ctx *ctx;
struct rt *krt;
int loglevel = LOG_INFO;
- bool change, result;
+ bool change;
assert(nrt != NULL);
ctx = nrt->rt_ifp->ctx;
sa_is_unspecified(&nrt->rt_netmask))
return false;
- krt = rb_tree_find_node(kroutes, nrt);
+ krt = rb_tree_find_node(&ctx->kroutes, nrt);
if (krt != NULL &&
krt->rt_ifp == nrt->rt_ifp &&
/* Only test flags dhcpcd controls */
#endif
if (change) {
- if (if_route(RTM_CHANGE, nrt) != -1) {
- result = true;
- goto out;
- }
+ if (if_route(RTM_CHANGE, nrt) != -1)
+ return true;
if (errno != ESRCH)
logerr("if_route (CHG)");
}
if (krt != NULL) {
if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)
logerr("if_route (DEL)");
+ else {
+ rb_tree_remove_node(&ctx->kroutes, krt);
+ rt_free(krt);
+ }
}
- result = true;
- goto out;
+ return true;
}
/* If the kernel claims the route exists we need to rip out the
if (krt != NULL) {
if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)
logerr("if_route (DEL)");
+ else {
+ rb_tree_remove_node(&ctx->kroutes, krt);
+ rt_free(krt);
+ }
}
#ifdef ROUTE_PER_GATEWAY
/* The OS allows many routes to the same dest with different gateways.
/* Shouldn't need to check for EEXIST, but some kernels don't
* dump the subnet route just after we added the address. */
- if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
- result = true;
- goto out;
- }
+ if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST)
+ return true;
#ifdef HAVE_ROUTE_METRIC
logerr:
#endif
logerr("if_route (ADD)");
-
-out:
- if (krt != NULL) {
- rb_tree_remove_node(kroutes, krt);
- rt_free(krt);
- }
- return result;
+ return false;
}
static bool
}
static bool
-rt_doroute(rb_tree_t *kroutes, struct rt *rt)
+rt_doroute(struct rt *rt)
{
struct dhcpcd_ctx *ctx;
struct rt *or;
#endif
rt_cmp_mtu(rt, or) != 0)
{
- if (!rt_add(kroutes, rt, or))
+ if (!rt_add(rt, or))
return false;
}
rb_tree_remove_node(&ctx->routes, or);
rt_free(or);
} else {
if (rt->rt_dflags & RTDF_FAKE) {
- or = rb_tree_find_node(kroutes, rt);
+ or = rb_tree_find_node(&ctx->kroutes, rt);
if (or == NULL)
return false;
if (rt_cmp(rt, or) == 0)
return false;
} else {
- if (!rt_add(kroutes, rt, NULL))
+ if (!rt_add(rt, NULL))
return false;
}
}
void
rt_build(struct dhcpcd_ctx *ctx, int af)
{
- rb_tree_t routes, added, kroutes;
+ rb_tree_t routes, added;
struct rt *rt, *rtn;
unsigned long long o;
rb_tree_init(&routes, &rt_compare_proto_ops);
rb_tree_init(&added, &rt_compare_os_ops);
- rb_tree_init(&kroutes, &rt_compare_os_ops);
- if (if_initrt(ctx, &kroutes, af) != 0)
- logerr("%s: if_initrt", __func__);
ctx->rt_order = 0;
ctx->options |= DHCPCD_RTBUILD;
/* Is this route already in our table? */
if (rb_tree_find_node(&added, rt) != NULL)
continue;
- if (rt_doroute(&kroutes, rt)) {
+ if (rt_doroute(rt)) {
rb_tree_remove_node(&routes, rt);
if (rb_tree_insert_node(&added, rt) != rt) {
errno = EEXIST;
getfail:
rt_headclear(&routes, AF_UNSPEC);
- rt_headclear(&kroutes, AF_UNSPEC);
}
extern const rb_tree_ops_t rt_compare_proto_ops;
void rt_init(struct dhcpcd_ctx *);
+void rt_discover(struct dhcpcd_ctx *);
void rt_dispose(struct dhcpcd_ctx *);
void rt_free(struct rt *);
void rt_freeif(struct interface *);
struct rt * rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *);
struct rt * rt_proto_add(rb_tree_t *, struct rt *);
int rt_cmp_dest(const struct rt *, const struct rt *);
-void rt_recvrt(int, const struct rt *, pid_t);
+void rt_recvrt(int, struct rt *, pid_t);
void rt_build(struct dhcpcd_ctx *, int);
#endif