route.c revision 1.1.1.16
198937Sdes/* SPDX-License-Identifier: BSD-2-Clause */
298937Sdes/*
398937Sdes * dhcpcd - route management
498937Sdes * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
598937Sdes * All rights reserved
698937Sdes
798937Sdes * Redistribution and use in source and binary forms, with or without
898937Sdes * modification, are permitted provided that the following conditions
998937Sdes * are met:
1098937Sdes * 1. Redistributions of source code must retain the above copyright
1198937Sdes *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <assert.h>
30#include <ctype.h>
31#include <errno.h>
32#include <stdbool.h>
33#include <stddef.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include "config.h"
39#include "common.h"
40#include "dhcpcd.h"
41#include "if.h"
42#include "if-options.h"
43#include "ipv4.h"
44#include "ipv4ll.h"
45#include "ipv6.h"
46#include "logerr.h"
47#include "route.h"
48#include "sa.h"
49
50/* Needed for NetBSD-6, 7 and 8. */
51#ifndef RB_TREE_FOREACH_SAFE
52#ifndef RB_TREE_PREV
53#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)
54#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)
55#endif
56#define RB_TREE_FOREACH_SAFE(N, T, S) \
57    for ((N) = RB_TREE_MIN(T); \
58        (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \
59        (N) = (S))
60#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
61    for ((N) = RB_TREE_MAX(T); \
62        (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \
63        (N) = (S))
64#endif
65
66#ifdef RT_FREE_ROUTE_TABLE_STATS
67static size_t croutes;
68static size_t nroutes;
69static size_t froutes;
70static size_t mroutes;
71#endif
72
73static void
74rt_maskedaddr(struct sockaddr *dst,
75	const struct sockaddr *addr, const struct sockaddr *netmask)
76{
77	const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
78	char *dstp = dst->sa_data;
79	const char *addre = (char *)dst + sa_len(addr);
80	const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
81
82	dst->sa_family = addr->sa_family;
83#ifdef HAVE_SA_LEN
84	dst->sa_len = addr->sa_len;
85#endif
86
87	if (sa_is_unspecified(netmask)) {
88		if (addre > dstp)
89			memcpy(dstp, addrp, (size_t)(addre - dstp));
90		return;
91	}
92
93	while (dstp < netmaske)
94		*dstp++ = *addrp++ & *netmaskp++;
95	if (dstp < addre)
96		memset(dstp, 0, (size_t)(addre - dstp));
97}
98
99int
100rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
101{
102	union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
103	union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
104
105	rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
106	rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
107	return sa_cmp(&ma1.sa, &ma2.sa);
108}
109
110/*
111 * On some systems, host routes have no need for a netmask.
112 * However DHCP specifies host routes using an all-ones netmask.
113 * This handy function allows easy comparison when the two
114 * differ.
115 */
116static int
117rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
118{
119
120	if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)
121		return 0;
122	return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
123}
124
125static int
126rt_compare_os(__unused void *context, const void *node1, const void *node2)
127{
128	const struct rt *rt1 = node1, *rt2 = node2;
129	int c;
130
131	/* Sort by masked destination. */
132	c = rt_cmp_dest(rt1, rt2);
133	if (c != 0)
134		return c;
135
136#ifdef HAVE_ROUTE_METRIC
137	c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
138#endif
139	return c;
140}
141
142static int
143rt_compare_list(__unused void *context, const void *node1, const void *node2)
144{
145	const struct rt *rt1 = node1, *rt2 = node2;
146
147	if (rt1->rt_order > rt2->rt_order)
148		return 1;
149	if (rt1->rt_order < rt2->rt_order)
150		return -1;
151	return 0;
152}
153
154static int
155rt_compare_proto(void *context, const void *node1, const void *node2)
156{
157	const struct rt *rt1 = node1, *rt2 = node2;
158	int c;
159	struct interface *ifp1, *ifp2;
160
161	assert(rt1->rt_ifp != NULL);
162	assert(rt2->rt_ifp != NULL);
163	ifp1 = rt1->rt_ifp;
164	ifp2 = rt2->rt_ifp;
165
166	/* Prefer interfaces with a carrier. */
167	c = ifp1->carrier - ifp2->carrier;
168	if (c != 0)
169		return -c;
170
171	/* Lower metric interfaces come first. */
172	c = (int)(ifp1->metric - ifp2->metric);
173	if (c != 0)
174		return c;
175
176	/* Finally the order in which the route was given to us. */
177	return rt_compare_list(context, rt1, rt2);
178}
179
180static const rb_tree_ops_t rt_compare_os_ops = {
181	.rbto_compare_nodes = rt_compare_os,
182	.rbto_compare_key = rt_compare_os,
183	.rbto_node_offset = offsetof(struct rt, rt_tree),
184	.rbto_context = NULL
185};
186
187const rb_tree_ops_t rt_compare_list_ops = {
188	.rbto_compare_nodes = rt_compare_list,
189	.rbto_compare_key = rt_compare_list,
190	.rbto_node_offset = offsetof(struct rt, rt_tree),
191	.rbto_context = NULL
192};
193
194const rb_tree_ops_t rt_compare_proto_ops = {
195	.rbto_compare_nodes = rt_compare_proto,
196	.rbto_compare_key = rt_compare_proto,
197	.rbto_node_offset = offsetof(struct rt, rt_tree),
198	.rbto_context = NULL
199};
200
201#ifdef RT_FREE_ROUTE_TABLE
202static int
203rt_compare_free(__unused void *context, const void *node1, const void *node2)
204{
205
206	return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
207}
208
209static const rb_tree_ops_t rt_compare_free_ops = {
210	.rbto_compare_nodes = rt_compare_free,
211	.rbto_compare_key = rt_compare_free,
212	.rbto_node_offset = offsetof(struct rt, rt_tree),
213	.rbto_context = NULL
214};
215#endif
216
217void
218rt_init(struct dhcpcd_ctx *ctx)
219{
220
221	rb_tree_init(&ctx->routes, &rt_compare_os_ops);
222#ifdef RT_FREE_ROUTE_TABLE
223	rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
224#endif
225}
226
227bool
228rt_is_default(const struct rt *rt)
229{
230
231	return sa_is_unspecified(&rt->rt_dest) &&
232	    sa_is_unspecified(&rt->rt_netmask);
233}
234
235static void
236rt_desc(const char *cmd, const struct rt *rt)
237{
238	char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
239	int prefix;
240	const char *ifname;
241	bool gateway_unspec;
242
243	assert(cmd != NULL);
244	assert(rt != NULL);
245
246	sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
247	prefix = sa_toprefix(&rt->rt_netmask);
248	sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
249	gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
250	ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
251
252	if (rt->rt_flags & RTF_HOST) {
253		if (gateway_unspec)
254			loginfox("%s: %s host route to %s",
255			    ifname, cmd, dest);
256		else
257			loginfox("%s: %s host route to %s via %s",
258			    ifname, cmd, dest, gateway);
259	} else if (rt_is_default(rt)) {
260		if (gateway_unspec)
261			loginfox("%s: %s default route",
262			    ifname, cmd);
263		else
264			loginfox("%s: %s default route via %s",
265			    ifname, cmd, gateway);
266	} else if (gateway_unspec)
267		loginfox("%s: %s%s route to %s/%d",
268		    ifname, cmd,
269		    rt->rt_flags & RTF_REJECT ? " reject" : "",
270		    dest, prefix);
271	else
272		loginfox("%s: %s%s route to %s/%d via %s",
273		    ifname, cmd,
274		    rt->rt_flags & RTF_REJECT ? " reject" : "",
275		    dest, prefix, gateway);
276}
277
278void
279rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
280{
281	struct rt *rt, *rtn;
282
283	if (rts == NULL)
284		return;
285	assert(ctx != NULL);
286#ifdef RT_FREE_ROUTE_TABLE
287	assert(&ctx->froutes != rts);
288#endif
289
290	RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
291		if (af != AF_UNSPEC &&
292		    rt->rt_dest.sa_family != af &&
293		    rt->rt_gateway.sa_family != af)
294			continue;
295		rb_tree_remove_node(rts, rt);
296		rt_free(rt);
297	}
298}
299
300void
301rt_headclear(rb_tree_t *rts, int af)
302{
303	struct rt *rt;
304
305	if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
306		return;
307	rt_headclear0(rt->rt_ifp->ctx, rts, af);
308}
309
310static void
311rt_headfree(rb_tree_t *rts)
312{
313	struct rt *rt;
314
315	while ((rt = RB_TREE_MIN(rts)) != NULL) {
316		rb_tree_remove_node(rts, rt);
317		free(rt);
318	}
319}
320
321void
322rt_dispose(struct dhcpcd_ctx *ctx)
323{
324
325	assert(ctx != NULL);
326	rt_headfree(&ctx->routes);
327#ifdef RT_FREE_ROUTE_TABLE
328	rt_headfree(&ctx->froutes);
329#ifdef RT_FREE_ROUTE_TABLE_STATS
330	logdebugx("free route list used %zu times", froutes);
331	logdebugx("new routes from route free list %zu", nroutes);
332	logdebugx("maximum route free list size %zu", mroutes);
333#endif
334#endif
335}
336
337struct rt *
338rt_new0(struct dhcpcd_ctx *ctx)
339{
340	struct rt *rt;
341
342	assert(ctx != NULL);
343#ifdef RT_FREE_ROUTE_TABLE
344	if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
345		rb_tree_remove_node(&ctx->froutes, rt);
346#ifdef RT_FREE_ROUTE_TABLE_STATS
347		croutes--;
348		nroutes++;
349#endif
350	} else
351#endif
352	if ((rt = malloc(sizeof(*rt))) == NULL) {
353		logerr(__func__);
354		return NULL;
355	}
356	memset(rt, 0, sizeof(*rt));
357	return rt;
358}
359
360void
361rt_setif(struct rt *rt, struct interface *ifp)
362{
363
364	assert(rt != NULL);
365	assert(ifp != NULL);
366	rt->rt_ifp = ifp;
367#ifdef HAVE_ROUTE_METRIC
368	rt->rt_metric = ifp->metric;
369#endif
370}
371
372struct rt *
373rt_new(struct interface *ifp)
374{
375	struct rt *rt;
376
377	assert(ifp != NULL);
378	if ((rt = rt_new0(ifp->ctx)) == NULL)
379		return NULL;
380	rt_setif(rt, ifp);
381	return rt;
382}
383
384struct rt *
385rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
386{
387
388	rt->rt_order = ctx->rt_order++;
389	if (rb_tree_insert_node(tree, rt) == rt)
390		return rt;
391
392	rt_free(rt);
393	return NULL;
394}
395
396struct rt *
397rt_proto_add(rb_tree_t *tree, struct rt *rt)
398{
399
400	assert (rt->rt_ifp != NULL);
401	return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
402}
403
404void
405rt_free(struct rt *rt)
406{
407#ifdef RT_FREE_ROUTE_TABLE
408	struct dhcpcd_ctx *ctx;
409
410	assert(rt != NULL);
411	if (rt->rt_ifp == NULL) {
412		free(rt);
413		return;
414	}
415
416	ctx = rt->rt_ifp->ctx;
417	rb_tree_insert_node(&ctx->froutes, rt);
418#ifdef RT_FREE_ROUTE_TABLE_STATS
419	croutes++;
420	froutes++;
421	if (croutes > mroutes)
422		mroutes = croutes;
423#endif
424#else
425	free(rt);
426#endif
427}
428
429void
430rt_freeif(struct interface *ifp)
431{
432	struct dhcpcd_ctx *ctx;
433	struct rt *rt, *rtn;
434
435	if (ifp == NULL)
436		return;
437	ctx = ifp->ctx;
438	RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
439		if (rt->rt_ifp == ifp) {
440			rb_tree_remove_node(&ctx->routes, rt);
441			rt_free(rt);
442		}
443	}
444}
445
446/* If something other than dhcpcd removes a route,
447 * we need to remove it from our internal table. */
448void
449rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
450{
451	struct dhcpcd_ctx *ctx;
452	struct rt *f;
453
454	assert(rt != NULL);
455	assert(rt->rt_ifp != NULL);
456	assert(rt->rt_ifp->ctx != NULL);
457
458	ctx = rt->rt_ifp->ctx;
459
460	switch(cmd) {
461	case RTM_DELETE:
462		f = rb_tree_find_node(&ctx->routes, rt);
463		if (f != NULL) {
464			char buf[32];
465
466			rb_tree_remove_node(&ctx->routes, f);
467			snprintf(buf, sizeof(buf), "pid %d deleted", pid);
468			rt_desc(buf, f);
469			rt_free(f);
470		}
471		break;
472	}
473
474#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
475	if (rt->rt_dest.sa_family == AF_INET)
476		ipv4ll_recvrt(cmd, rt);
477#endif
478}
479
480static bool
481rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
482{
483	struct dhcpcd_ctx *ctx;
484	bool change, kroute, result;
485
486	assert(nrt != NULL);
487	ctx = nrt->rt_ifp->ctx;
488
489	/*
490	 * Don't install a gateway if not asked to.
491	 * This option is mainly for VPN users who want their VPN to be the
492	 * default route.
493	 * Because VPN's generally don't care about route management
494	 * beyond their own, a longer term solution would be to remove this
495	 * and get the VPN to inject the default route into dhcpcd somehow.
496	 */
497	if (((nrt->rt_ifp->active &&
498	    !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
499	    (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
500	    sa_is_unspecified(&nrt->rt_dest) &&
501	    sa_is_unspecified(&nrt->rt_netmask))
502		return false;
503
504	rt_desc(ort == NULL ? "adding" : "changing", nrt);
505
506	change = kroute = result = false;
507	if (ort == NULL) {
508		ort = rb_tree_find_node(kroutes, nrt);
509		if (ort != NULL &&
510		    ((ort->rt_flags & RTF_REJECT &&
511		      nrt->rt_flags & RTF_REJECT) ||
512		     (ort->rt_ifp == nrt->rt_ifp &&
513#ifdef HAVE_ROUTE_METRIC
514		    ort->rt_metric == nrt->rt_metric &&
515#endif
516		    sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
517		{
518			if (ort->rt_mtu == nrt->rt_mtu)
519				return true;
520			change = true;
521			kroute = true;
522		}
523	} else if (ort->rt_dflags & RTDF_FAKE &&
524	    !(nrt->rt_dflags & RTDF_FAKE) &&
525	    ort->rt_ifp == nrt->rt_ifp &&
526#ifdef HAVE_ROUTE_METRIC
527	    ort->rt_metric == nrt->rt_metric &&
528#endif
529	    sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
530	    rt_cmp_netmask(ort, nrt) == 0 &&
531	    sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
532	{
533		if (ort->rt_mtu == nrt->rt_mtu)
534			return true;
535		change = true;
536	}
537
538#ifdef RTF_CLONING
539	/* BSD can set routes to be cloning routes.
540	 * Cloned routes inherit the parent flags.
541	 * As such, we need to delete and re-add the route to flush children
542	 * to correct the flags. */
543	if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
544		change = false;
545#endif
546
547	if (change) {
548		if (if_route(RTM_CHANGE, nrt) != -1) {
549			result = true;
550			goto out;
551		}
552		if (errno != ESRCH)
553			logerr("if_route (CHG)");
554	}
555
556#ifdef HAVE_ROUTE_METRIC
557	/* With route metrics, we can safely add the new route before
558	 * deleting the old route. */
559	if (if_route(RTM_ADD, nrt) != -1) {
560		if (ort != NULL) {
561			if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
562				logerr("if_route (DEL)");
563		}
564		result = true;
565		goto out;
566	}
567
568	/* If the kernel claims the route exists we need to rip out the
569	 * old one first. */
570	if (errno != EEXIST || ort == NULL)
571		goto logerr;
572#endif
573
574	/* No route metrics, we need to delete the old route before
575	 * adding the new one. */
576#ifdef ROUTE_PER_GATEWAY
577	errno = 0;
578#endif
579	if (ort != NULL) {
580		if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
581			logerr("if_route (DEL)");
582		else
583			kroute = false;
584	}
585#ifdef ROUTE_PER_GATEWAY
586	/* The OS allows many routes to the same dest with different gateways.
587	 * dhcpcd does not support this yet, so for the time being just keep on
588	 * deleting the route until there is an error. */
589	if (ort != NULL && errno == 0) {
590		for (;;) {
591			if (if_route(RTM_DELETE, ort) == -1)
592				break;
593		}
594	}
595#endif
596
597	/* Shouldn't need to check for EEXIST, but some kernels don't
598	 * dump the subnet route just after we added the address. */
599	if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
600		result = true;
601		goto out;
602	}
603
604#ifdef HAVE_ROUTE_METRIC
605logerr:
606#endif
607	logerr("if_route (ADD)");
608
609out:
610	if (kroute) {
611		rb_tree_remove_node(kroutes, ort);
612		rt_free(ort);
613	}
614	return result;
615}
616
617static bool
618rt_delete(struct rt *rt)
619{
620	int retval;
621
622	rt_desc("deleting", rt);
623	retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
624	if (!retval && errno != ENOENT && errno != ESRCH)
625		logerr(__func__);
626	return retval;
627}
628
629static bool
630rt_cmp(const struct rt *r1, const struct rt *r2)
631{
632
633	return (r1->rt_ifp == r2->rt_ifp &&
634#ifdef HAVE_ROUTE_METRIC
635	    r1->rt_metric == r2->rt_metric &&
636#endif
637	    sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
638}
639
640static bool
641rt_doroute(rb_tree_t *kroutes, struct rt *rt)
642{
643	struct dhcpcd_ctx *ctx;
644	struct rt *or;
645
646	ctx = rt->rt_ifp->ctx;
647	/* Do we already manage it? */
648	or = rb_tree_find_node(&ctx->routes, rt);
649	if (or != NULL) {
650		if (rt->rt_dflags & RTDF_FAKE)
651			return true;
652		if (or->rt_dflags & RTDF_FAKE ||
653		    !rt_cmp(rt, or) ||
654		    (rt->rt_ifa.sa_family != AF_UNSPEC &&
655		    sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
656		    or->rt_mtu != rt->rt_mtu)
657		{
658			if (!rt_add(kroutes, rt, or))
659				return false;
660		}
661		rb_tree_remove_node(&ctx->routes, or);
662		rt_free(or);
663	} else {
664		if (rt->rt_dflags & RTDF_FAKE) {
665			or = rb_tree_find_node(kroutes, rt);
666			if (or == NULL)
667				return false;
668			if (!rt_cmp(rt, or))
669				return false;
670		} else {
671			if (!rt_add(kroutes, rt, NULL))
672				return false;
673		}
674	}
675
676	return true;
677}
678
679void
680rt_build(struct dhcpcd_ctx *ctx, int af)
681{
682	rb_tree_t routes, added, kroutes;
683	struct rt *rt, *rtn;
684	unsigned long long o;
685
686	rb_tree_init(&routes, &rt_compare_proto_ops);
687	rb_tree_init(&added, &rt_compare_os_ops);
688	rb_tree_init(&kroutes, &rt_compare_os_ops);
689	if_initrt(ctx, &kroutes, af);
690	ctx->rt_order = 0;
691	ctx->options |= DHCPCD_RTBUILD;
692
693#ifdef INET
694	if (!inet_getroutes(ctx, &routes))
695		goto getfail;
696#endif
697#ifdef INET6
698	if (!inet6_getroutes(ctx, &routes))
699		goto getfail;
700#endif
701
702#ifdef BSD
703	/* Rewind the miss filter */
704	ctx->rt_missfilterlen = 0;
705#endif
706
707	RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
708#ifdef BSD
709		if (rt_is_default(rt) &&
710		    if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
711			logerr("if_missfilter");
712#endif
713		if ((rt->rt_dest.sa_family != af &&
714		    rt->rt_dest.sa_family != AF_UNSPEC) ||
715		    (rt->rt_gateway.sa_family != af &&
716		    rt->rt_gateway.sa_family != AF_UNSPEC))
717			continue;
718		/* Is this route already in our table? */
719		if (rb_tree_find_node(&added, rt) != NULL)
720			continue;
721		if (rt_doroute(&kroutes, rt)) {
722			rb_tree_remove_node(&routes, rt);
723			if (rb_tree_insert_node(&added, rt) != rt) {
724				errno = EEXIST;
725				logerr(__func__);
726				rt_free(rt);
727			}
728		}
729	}
730
731#ifdef BSD
732	if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
733		logerr("if_missfilter_apply");
734#endif
735
736	/* Remove old routes we used to manage. */
737	RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
738		if ((rt->rt_dest.sa_family != af &&
739		    rt->rt_dest.sa_family != AF_UNSPEC) ||
740		    (rt->rt_gateway.sa_family != af &&
741		    rt->rt_gateway.sa_family != AF_UNSPEC))
742			continue;
743		rb_tree_remove_node(&ctx->routes, rt);
744		if (rb_tree_find_node(&added, rt) == NULL) {
745			o = rt->rt_ifp->options ?
746			    rt->rt_ifp->options->options :
747			    ctx->options;
748			if ((o &
749				(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
750				(DHCPCD_EXITING | DHCPCD_PERSISTENT))
751				rt_delete(rt);
752		}
753		rt_free(rt);
754	}
755
756	/* XXX This needs to be optimised. */
757	while ((rt = RB_TREE_MIN(&added)) != NULL) {
758		rb_tree_remove_node(&added, rt);
759		if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
760			errno = EEXIST;
761			logerr(__func__);
762			rt_free(rt);
763		}
764	}
765
766
767getfail:
768	rt_headclear(&routes, AF_UNSPEC);
769	rt_headclear(&kroutes, AF_UNSPEC);
770}
771