This is an attempt to reduce the memory dhcpcd uses.
By removing kroutes and froutes from global context.
kroutes are generated at the start of rt_build and freed at
the end.
const struct if_options *ifo;
uint8_t len;
char buf[(sizeof(ifo->clientid) - 1) * 3];
- int r;
- r = dhcp_initstate(ifp);
- if (r == -1)
+ if (dhcp_initstate(ifp) == -1)
return -1;
- else if (r == 1) {
- /* Now is a good time to find IPv4 routes */
- if_initrt(ifp->ctx, AF_INET);
- }
state = D_STATE(ifp);
state->state = DHS_INIT;
}
/* Now all addresses have been added, rebuild the routing table. */
- if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
}
state = D6_STATE(ifp);
state->state = DH6S_DELEGATED;
ipv6_addaddrs(&state->addrs);
- if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
dhcp6_script_try_run(ifp, 1);
}
else
lognewinfo("%s: expire in %"PRIu32" seconds",
ifp->name, state->expire);
- if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
if (!timed_out)
dhcp6_writelease(ifp);
struct if_head *ifaces;
rb_tree_t routes; /* our routes */
- rb_tree_t kroutes; /* all kernel routes */
+#ifdef RT_FREE_ROUTE_TABLE
rb_tree_t froutes; /* free routes for re-use */
+#endif
int pf_inet_fd;
void *priv;
}
int
-if_initrt(struct dhcpcd_ctx *ctx, int af)
+if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
{
struct rt_msghdr *rtm;
int mib[6];
size_t needed;
char *buf, *p, *end;
- struct rt rt;
-
- rt_headclear(&ctx->kroutes, af);
+ struct rt rt, *rtn;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
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);
+ if (if_copyrt(ctx, &rt, rtm) != 0)
+ 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);
}
free(buf);
return 0;
static int
get_netlink(struct dhcpcd_ctx *ctx, struct iovec *iov,
- struct interface *ifp, int fd, int flags,
- int (*callback)(struct dhcpcd_ctx *, struct interface *, struct nlmsghdr *))
+ void *arg, int fd, int flags,
+ int (*callback)(struct dhcpcd_ctx *, void *, struct nlmsghdr *))
{
struct sockaddr_nl nladdr = { .nl_pid = 0 };
struct msghdr msg = {
again = 0;
break;
}
- if (callback && (r = callback(ctx, ifp, nlm)) != 0)
+ if (callback && (r = callback(ctx, arg, nlm)) != 0)
break;
}
}
static int
-send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp,
+send_netlink(struct dhcpcd_ctx *ctx, void *arg,
int protocol, struct nlmsghdr *hdr,
- int (*callback)(struct dhcpcd_ctx *, struct interface *, struct nlmsghdr *))
+ int (*callback)(struct dhcpcd_ctx *, void *, struct nlmsghdr *))
{
int s, r;
struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
.iov_len = sizeof(buf),
};
- r = get_netlink(ctx, &riov, ifp, s, 0, callback);
+ r = get_netlink(ctx, &riov, arg, s, 0, callback);
} else
r = -1;
if (protocol != NETLINK_ROUTE)
}
static int
-_gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+_gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused void *arg,
struct nlmsghdr *nlm)
{
struct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];
}
static int
-_if_getssid_nl80211(__unused struct dhcpcd_ctx *ctx, struct interface *ifp,
+_if_getssid_nl80211(__unused struct dhcpcd_ctx *ctx, void *arg,
struct nlmsghdr *nlm)
{
+ struct interface *ifp = arg;
struct nlattr *tb[NL80211_ATTR_BSS + 1];
struct nlattr *bss[NL80211_BSS_STATUS + 1];
uint32_t status;
}
static int
-_if_initrt(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
+_if_initrt(struct dhcpcd_ctx *ctx, void *arg,
struct nlmsghdr *nlm)
{
- struct rt rt;
+ struct rt rt, *rtn;
+ rb_tree_t *kroutes = arg;
- if (if_copyrt(ctx, &rt, nlm) == 0) {
- rt.rt_dflags |= RTDF_INIT;
- rt_recvrt(RTM_ADD, &rt);
+ if (if_copyrt(ctx, &rt, nlm) != 0)
+ return 0;
+ if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
+ logerr(__func__);
+ return 0;
}
+ memcpy(rtn, &rt, sizeof(*rtn));
+ if (rb_tree_insert_node(kroutes, rtn) != rtn)
+ rt_free(rtn);
return 0;
}
int
-if_initrt(struct dhcpcd_ctx *ctx, int af)
+if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
{
struct nlmr nlm = {
.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
};
rt_headclear(&ctx->kroutes, af);
- return send_netlink(ctx, NULL, NETLINK_ROUTE, &nlm.hdr, &_if_initrt);
+ return send_netlink(ctx, kroutes, NETLINK_ROUTE, &nlm.hdr, &_if_initrt);
}
#endif
int if_route(unsigned char, const struct rt *rt);
-int if_initrt(struct dhcpcd_ctx *, int);
+int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
#ifdef INET
int if_address(unsigned char, const struct ipv4_addr *);
state->addr = ia;
state->added = STATE_ADDED;
- /* 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_initrt(ifp->ctx, AF_INET);
rt_build(ifp->ctx, AF_INET);
#ifdef ARP
return;
}
timespecclear(&state->defend);
- if_initrt(ifp->ctx, AF_INET);
rt_build(ifp->ctx, AF_INET);
arp_announce(astate);
script_runreason(ifp, "IPV4LL");
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 (sa_is_unspecified(&rt->rt_dest))
return 0;
ctx = rt->rt_ifp->ctx;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (IPV4LL_STATE_RUNNING(ifp)) {
- if_initrt(ctx, AF_INET);
rt_build(ctx, AF_INET);
break;
}
ia->prefix_pltime = ND6_INFINITE_LIFETIME;
ia->dadcallback = ipv6_staticdadcallback;
ipv6_addaddr(ia, NULL);
- if_initrt(ifp->ctx, AF_INET6);
rt_build(ifp->ctx, AF_INET6);
if (run_script)
script_runreason(ifp, "STATIC6");
ipv6_regentempifid(ifp);
}
- /* Load existing routes */
- if_initrt(ifp->ctx, AF_INET6);
return 0;
}
ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL);
if (drop) {
- if (ifp->ctx->ra_routers != NULL) {
- if_initrt(ifp->ctx, AF_INET6);
+ if (ifp->ctx->ra_routers != NULL)
rt_build(ifp->ctx, AF_INET6);
- }
} else {
/* Because we need to cache the addresses we don't control,
* we only free the state on when NOT dropping addresses. */
ipv6_addtempaddrs(ifp, &rap->acquired);
#endif
- /* 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_initrt(ifp->ctx, AF_INET6);
-
rt_build(ifp->ctx, AF_INET6);
if (ipv6nd_scriptrun(rap))
return;
.rbto_context = NULL
};
+#ifdef RT_FREE_ROUTE_TABLE
static int
rt_compare_free(__unused void *context, const void *node1, const void *node2)
{
.rbto_node_offset = offsetof(struct rt, rt_tree),
.rbto_context = NULL
};
+#endif
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
}
static void
if (rts == NULL)
return;
assert(ctx != NULL);
+#ifdef RT_FREE_ROUTE_TABLE
assert(&ctx->froutes != rts);
+#endif
RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
if (af != AF_UNSPEC &&
assert(ctx != NULL);
rt_headfree(&ctx->routes);
- rt_headfree(&ctx->kroutes);
+#ifdef RT_FREE_ROUTE_TABLE
rt_headfree(&ctx->froutes);
+#endif
}
struct rt *
struct rt *rt;
assert(ctx != NULL);
+#ifdef RT_FREE_ROUTE_TABLE
if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL)
rb_tree_remove_node(&ctx->froutes, rt);
- else if ((rt = malloc(sizeof(*rt))) == NULL) {
+ else
+#endif
+ if ((rt = malloc(sizeof(*rt))) == NULL) {
logerr(__func__);
return NULL;
}
void
rt_free(struct rt *rt)
{
+#ifdef RT_FREE_ROUTE_TABLE
struct dhcpcd_ctx *ctx;
assert(rt != NULL);
ctx = rt->rt_ifp->ctx;
rb_tree_insert_node(&ctx->froutes, rt);
+#else
+ free(rt);
+#endif
}
void
rt_free(rt);
}
}
- RB_TREE_FOREACH_SAFE(rt, &ctx->kroutes, rtn) {
- if (rt->rt_ifp == ifp) {
- rb_tree_remove_node(&ctx->kroutes, rt);
- rt_free(rt);
- }
- }
-}
-
-static void
-rt_kfree(struct rt *rt)
-{
- struct dhcpcd_ctx *ctx;
- struct rt *f;
-
- assert(rt != NULL);
- ctx = rt->rt_ifp->ctx;
- f = rb_tree_find_node(&ctx->kroutes, rt);
- if (f == NULL)
- return;
- rb_tree_remove_node(&ctx->kroutes, f);
- rt_free(f);
}
/* If something other than dhcpcd removes a route,
switch(cmd) {
case RTM_DELETE:
- f = rb_tree_find_node(&ctx->kroutes, rt);
- if (f != NULL) {
- rb_tree_remove_node(&ctx->kroutes, f);
- rt_free(f);
- }
f = rb_tree_find_node(&ctx->routes, rt);
if (f != NULL) {
rb_tree_remove_node(&ctx->routes, f);
rt_free(f);
}
break;
- case RTM_ADD:
- if ((f = rt_new(rt->rt_ifp)) == NULL)
- break;
- memcpy(f, rt, sizeof(*f));
- if (rb_tree_insert_node(&ctx->kroutes, f) != f)
- rt_free(f);
- break;
}
#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
}
static bool
-rt_add(struct rt *nrt, struct rt *ort)
+rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
{
struct dhcpcd_ctx *ctx;
bool change;
change = false;
if (ort == NULL) {
- ort = rb_tree_find_node(&ctx->kroutes, nrt);
+ ort = rb_tree_find_node(kroutes, nrt);
if (ort != NULL &&
((ort->rt_flags & RTF_REJECT &&
nrt->rt_flags & RTF_REJECT) ||
if (ort != NULL) {
if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
logerr("if_route (DEL)");
- else
- rt_kfree(ort);
}
#ifdef ROUTE_PER_GATEWAY
/* The OS allows many routes to the same dest with different gateways.
}
}
#endif
- if (if_route(RTM_ADD, nrt) != -1)
+ if (if_route(RTM_ADD, nrt) != -1) {
+ if (ort != NULL)
+ memcpy(ort, nrt, sizeof(*ort));
return true;
+ }
#ifdef HAVE_ROUTE_METRIC
logerr:
#endif
retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
if (!retval && errno != ENOENT && errno != ESRCH)
logerr(__func__);
- /* 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_doroute(struct rt *rt)
+rt_doroute(rb_tree_t *kroutes, struct rt *rt)
{
struct dhcpcd_ctx *ctx;
struct rt *or;
sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
or->rt_mtu != rt->rt_mtu)
{
- if (!rt_add(rt, or))
+ if (!rt_add(kroutes, 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(&ctx->kroutes, rt);
+ or = rb_tree_find_node(kroutes, rt);
if (or == NULL)
return false;
if (!rt_cmp(rt, or))
return false;
} else {
- if (!rt_add(rt, NULL))
+ if (!rt_add(kroutes, rt, NULL))
return false;
}
}
void
rt_build(struct dhcpcd_ctx *ctx, int af)
{
- rb_tree_t routes, added;
+ rb_tree_t kroutes, routes, added;
struct rt *rt, *rtn;
unsigned long long o;
+ rb_tree_init(&kroutes, &rt_compare_os_ops);
rb_tree_init(&routes, &rt_compare_list_ops);
rb_tree_init(&added, &rt_compare_os_ops);
#endif
}
+ if_initrt(ctx, &kroutes, af);
+
RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
if (rt->rt_dest.sa_family != af &&
rt->rt_gateway.sa_family != af)
/* Is this route already in our table? */
if (rb_tree_find_node(&added, rt) != NULL)
continue;
- if (rt_doroute(rt)) {
+ if (rt_doroute(&kroutes, 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);
}
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 */
#define RTDF_RA 0x08 /* Router Advertisement */