1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * dhcpcd - route management 4 * Copyright (c) 2006-2020 Roy Marples <roy@marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * 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 67 static size_t croutes; 68 static size_t nroutes; 69 static size_t froutes; 70 static size_t mroutes; 71 #endif 72 73 static void 74 rt_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 99 int 100 rt_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 */ 116 static int 117 rt_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 125 static int 126 rt_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 142 static int 143 rt_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 154 static int 155 rt_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 #ifdef INET 172 /* IPv4LL routes always come last */ 173 if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL)) 174 return -1; 175 else if (!(rt1->rt_dflags & RTDF_IPV4LL) && rt2->rt_dflags & RTDF_IPV4LL) 176 return 1; 177 #endif 178 179 /* Lower metric interfaces come first. */ 180 c = (int)(ifp1->metric - ifp2->metric); 181 if (c != 0) 182 return c; 183 184 /* Finally the order in which the route was given to us. */ 185 return rt_compare_list(context, rt1, rt2); 186 } 187 188 static const rb_tree_ops_t rt_compare_os_ops = { 189 .rbto_compare_nodes = rt_compare_os, 190 .rbto_compare_key = rt_compare_os, 191 .rbto_node_offset = offsetof(struct rt, rt_tree), 192 .rbto_context = NULL 193 }; 194 195 const rb_tree_ops_t rt_compare_list_ops = { 196 .rbto_compare_nodes = rt_compare_list, 197 .rbto_compare_key = rt_compare_list, 198 .rbto_node_offset = offsetof(struct rt, rt_tree), 199 .rbto_context = NULL 200 }; 201 202 const rb_tree_ops_t rt_compare_proto_ops = { 203 .rbto_compare_nodes = rt_compare_proto, 204 .rbto_compare_key = rt_compare_proto, 205 .rbto_node_offset = offsetof(struct rt, rt_tree), 206 .rbto_context = NULL 207 }; 208 209 #ifdef RT_FREE_ROUTE_TABLE 210 static int 211 rt_compare_free(__unused void *context, const void *node1, const void *node2) 212 { 213 214 return node1 == node2 ? 0 : node1 < node2 ? -1 : 1; 215 } 216 217 static const rb_tree_ops_t rt_compare_free_ops = { 218 .rbto_compare_nodes = rt_compare_free, 219 .rbto_compare_key = rt_compare_free, 220 .rbto_node_offset = offsetof(struct rt, rt_tree), 221 .rbto_context = NULL 222 }; 223 #endif 224 225 void 226 rt_init(struct dhcpcd_ctx *ctx) 227 { 228 229 rb_tree_init(&ctx->routes, &rt_compare_os_ops); 230 #ifdef RT_FREE_ROUTE_TABLE 231 rb_tree_init(&ctx->froutes, &rt_compare_free_ops); 232 #endif 233 } 234 235 bool 236 rt_is_default(const struct rt *rt) 237 { 238 239 return sa_is_unspecified(&rt->rt_dest) && 240 sa_is_unspecified(&rt->rt_netmask); 241 } 242 243 static void 244 rt_desc(const char *cmd, const struct rt *rt) 245 { 246 char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN]; 247 int prefix; 248 const char *ifname; 249 bool gateway_unspec; 250 251 assert(cmd != NULL); 252 assert(rt != NULL); 253 254 sa_addrtop(&rt->rt_dest, dest, sizeof(dest)); 255 prefix = sa_toprefix(&rt->rt_netmask); 256 sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway)); 257 gateway_unspec = sa_is_unspecified(&rt->rt_gateway); 258 ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name; 259 260 if (rt->rt_flags & RTF_HOST) { 261 if (gateway_unspec) 262 loginfox("%s: %s host route to %s", 263 ifname, cmd, dest); 264 else 265 loginfox("%s: %s host route to %s via %s", 266 ifname, cmd, dest, gateway); 267 } else if (rt_is_default(rt)) { 268 if (gateway_unspec) 269 loginfox("%s: %s default route", 270 ifname, cmd); 271 else 272 loginfox("%s: %s default route via %s", 273 ifname, cmd, gateway); 274 } else if (gateway_unspec) 275 loginfox("%s: %s%s route to %s/%d", 276 ifname, cmd, 277 rt->rt_flags & RTF_REJECT ? " reject" : "", 278 dest, prefix); 279 else 280 loginfox("%s: %s%s route to %s/%d via %s", 281 ifname, cmd, 282 rt->rt_flags & RTF_REJECT ? " reject" : "", 283 dest, prefix, gateway); 284 } 285 286 void 287 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af) 288 { 289 struct rt *rt, *rtn; 290 291 if (rts == NULL) 292 return; 293 assert(ctx != NULL); 294 #ifdef RT_FREE_ROUTE_TABLE 295 assert(&ctx->froutes != rts); 296 #endif 297 298 RB_TREE_FOREACH_SAFE(rt, rts, rtn) { 299 if (af != AF_UNSPEC && 300 rt->rt_dest.sa_family != af && 301 rt->rt_gateway.sa_family != af) 302 continue; 303 rb_tree_remove_node(rts, rt); 304 rt_free(rt); 305 } 306 } 307 308 void 309 rt_headclear(rb_tree_t *rts, int af) 310 { 311 struct rt *rt; 312 313 if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL) 314 return; 315 rt_headclear0(rt->rt_ifp->ctx, rts, af); 316 } 317 318 static void 319 rt_headfree(rb_tree_t *rts) 320 { 321 struct rt *rt; 322 323 while ((rt = RB_TREE_MIN(rts)) != NULL) { 324 rb_tree_remove_node(rts, rt); 325 free(rt); 326 } 327 } 328 329 void 330 rt_dispose(struct dhcpcd_ctx *ctx) 331 { 332 333 assert(ctx != NULL); 334 rt_headfree(&ctx->routes); 335 #ifdef RT_FREE_ROUTE_TABLE 336 rt_headfree(&ctx->froutes); 337 #ifdef RT_FREE_ROUTE_TABLE_STATS 338 logdebugx("free route list used %zu times", froutes); 339 logdebugx("new routes from route free list %zu", nroutes); 340 logdebugx("maximum route free list size %zu", mroutes); 341 #endif 342 #endif 343 } 344 345 struct rt * 346 rt_new0(struct dhcpcd_ctx *ctx) 347 { 348 struct rt *rt; 349 350 assert(ctx != NULL); 351 #ifdef RT_FREE_ROUTE_TABLE 352 if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) { 353 rb_tree_remove_node(&ctx->froutes, rt); 354 #ifdef RT_FREE_ROUTE_TABLE_STATS 355 croutes--; 356 nroutes++; 357 #endif 358 } else 359 #endif 360 if ((rt = malloc(sizeof(*rt))) == NULL) { 361 logerr(__func__); 362 return NULL; 363 } 364 memset(rt, 0, sizeof(*rt)); 365 return rt; 366 } 367 368 void 369 rt_setif(struct rt *rt, struct interface *ifp) 370 { 371 372 assert(rt != NULL); 373 assert(ifp != NULL); 374 rt->rt_ifp = ifp; 375 #ifdef HAVE_ROUTE_METRIC 376 rt->rt_metric = ifp->metric; 377 #endif 378 } 379 380 struct rt * 381 rt_new(struct interface *ifp) 382 { 383 struct rt *rt; 384 385 assert(ifp != NULL); 386 if ((rt = rt_new0(ifp->ctx)) == NULL) 387 return NULL; 388 rt_setif(rt, ifp); 389 return rt; 390 } 391 392 struct rt * 393 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx) 394 { 395 396 rt->rt_order = ctx->rt_order++; 397 if (rb_tree_insert_node(tree, rt) == rt) 398 return rt; 399 400 rt_free(rt); 401 return NULL; 402 } 403 404 struct rt * 405 rt_proto_add(rb_tree_t *tree, struct rt *rt) 406 { 407 408 assert (rt->rt_ifp != NULL); 409 return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx); 410 } 411 412 void 413 rt_free(struct rt *rt) 414 { 415 #ifdef RT_FREE_ROUTE_TABLE 416 struct dhcpcd_ctx *ctx; 417 418 assert(rt != NULL); 419 if (rt->rt_ifp == NULL) { 420 free(rt); 421 return; 422 } 423 424 ctx = rt->rt_ifp->ctx; 425 rb_tree_insert_node(&ctx->froutes, rt); 426 #ifdef RT_FREE_ROUTE_TABLE_STATS 427 croutes++; 428 froutes++; 429 if (croutes > mroutes) 430 mroutes = croutes; 431 #endif 432 #else 433 free(rt); 434 #endif 435 } 436 437 void 438 rt_freeif(struct interface *ifp) 439 { 440 struct dhcpcd_ctx *ctx; 441 struct rt *rt, *rtn; 442 443 if (ifp == NULL) 444 return; 445 ctx = ifp->ctx; 446 RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) { 447 if (rt->rt_ifp == ifp) { 448 rb_tree_remove_node(&ctx->routes, rt); 449 rt_free(rt); 450 } 451 } 452 } 453 454 /* If something other than dhcpcd removes a route, 455 * we need to remove it from our internal table. */ 456 void 457 rt_recvrt(int cmd, const struct rt *rt, pid_t pid) 458 { 459 struct dhcpcd_ctx *ctx; 460 struct rt *f; 461 462 assert(rt != NULL); 463 assert(rt->rt_ifp != NULL); 464 assert(rt->rt_ifp->ctx != NULL); 465 466 ctx = rt->rt_ifp->ctx; 467 468 switch(cmd) { 469 case RTM_DELETE: 470 f = rb_tree_find_node(&ctx->routes, rt); 471 if (f != NULL) { 472 char buf[32]; 473 474 rb_tree_remove_node(&ctx->routes, f); 475 snprintf(buf, sizeof(buf), "pid %d deleted", pid); 476 rt_desc(buf, f); 477 rt_free(f); 478 } 479 break; 480 } 481 482 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC) 483 if (rt->rt_dest.sa_family == AF_INET) 484 ipv4ll_recvrt(cmd, rt); 485 #endif 486 } 487 488 static bool 489 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort) 490 { 491 struct dhcpcd_ctx *ctx; 492 bool change, kroute, result; 493 494 assert(nrt != NULL); 495 ctx = nrt->rt_ifp->ctx; 496 497 /* 498 * Don't install a gateway if not asked to. 499 * This option is mainly for VPN users who want their VPN to be the 500 * default route. 501 * Because VPN's generally don't care about route management 502 * beyond their own, a longer term solution would be to remove this 503 * and get the VPN to inject the default route into dhcpcd somehow. 504 */ 505 if (((nrt->rt_ifp->active && 506 !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) || 507 (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) && 508 sa_is_unspecified(&nrt->rt_dest) && 509 sa_is_unspecified(&nrt->rt_netmask)) 510 return false; 511 512 rt_desc(ort == NULL ? "adding" : "changing", nrt); 513 514 change = kroute = result = false; 515 if (ort == NULL) { 516 ort = rb_tree_find_node(kroutes, nrt); 517 if (ort != NULL && 518 ((ort->rt_flags & RTF_REJECT && 519 nrt->rt_flags & RTF_REJECT) || 520 (ort->rt_ifp == nrt->rt_ifp && 521 #ifdef HAVE_ROUTE_METRIC 522 ort->rt_metric == nrt->rt_metric && 523 #endif 524 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0))) 525 { 526 if (ort->rt_mtu == nrt->rt_mtu) 527 return true; 528 change = true; 529 kroute = true; 530 } 531 } else if (ort->rt_dflags & RTDF_FAKE && 532 !(nrt->rt_dflags & RTDF_FAKE) && 533 ort->rt_ifp == nrt->rt_ifp && 534 #ifdef HAVE_ROUTE_METRIC 535 ort->rt_metric == nrt->rt_metric && 536 #endif 537 sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 && 538 rt_cmp_netmask(ort, nrt) == 0 && 539 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0) 540 { 541 if (ort->rt_mtu == nrt->rt_mtu) 542 return true; 543 change = true; 544 } 545 546 #ifdef RTF_CLONING 547 /* BSD can set routes to be cloning routes. 548 * Cloned routes inherit the parent flags. 549 * As such, we need to delete and re-add the route to flush children 550 * to correct the flags. */ 551 if (change && ort != NULL && ort->rt_flags & RTF_CLONING) 552 change = false; 553 #endif 554 555 if (change) { 556 if (if_route(RTM_CHANGE, nrt) != -1) { 557 result = true; 558 goto out; 559 } 560 if (errno != ESRCH) 561 logerr("if_route (CHG)"); 562 } 563 564 #ifdef HAVE_ROUTE_METRIC 565 /* With route metrics, we can safely add the new route before 566 * deleting the old route. */ 567 if (if_route(RTM_ADD, nrt) != -1) { 568 if (ort != NULL) { 569 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH) 570 logerr("if_route (DEL)"); 571 } 572 result = true; 573 goto out; 574 } 575 576 /* If the kernel claims the route exists we need to rip out the 577 * old one first. */ 578 if (errno != EEXIST || ort == NULL) 579 goto logerr; 580 #endif 581 582 /* No route metrics, we need to delete the old route before 583 * adding the new one. */ 584 #ifdef ROUTE_PER_GATEWAY 585 errno = 0; 586 #endif 587 if (ort != NULL) { 588 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH) 589 logerr("if_route (DEL)"); 590 else 591 kroute = false; 592 } 593 #ifdef ROUTE_PER_GATEWAY 594 /* The OS allows many routes to the same dest with different gateways. 595 * dhcpcd does not support this yet, so for the time being just keep on 596 * deleting the route until there is an error. */ 597 if (ort != NULL && errno == 0) { 598 for (;;) { 599 if (if_route(RTM_DELETE, ort) == -1) 600 break; 601 } 602 } 603 #endif 604 605 /* Shouldn't need to check for EEXIST, but some kernels don't 606 * dump the subnet route just after we added the address. */ 607 if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) { 608 result = true; 609 goto out; 610 } 611 612 #ifdef HAVE_ROUTE_METRIC 613 logerr: 614 #endif 615 logerr("if_route (ADD)"); 616 617 out: 618 if (kroute) { 619 rb_tree_remove_node(kroutes, ort); 620 rt_free(ort); 621 } 622 return result; 623 } 624 625 static bool 626 rt_delete(struct rt *rt) 627 { 628 int retval; 629 630 rt_desc("deleting", rt); 631 retval = if_route(RTM_DELETE, rt) == -1 ? false : true; 632 if (!retval && errno != ENOENT && errno != ESRCH) 633 logerr(__func__); 634 return retval; 635 } 636 637 static bool 638 rt_cmp(const struct rt *r1, const struct rt *r2) 639 { 640 641 return (r1->rt_ifp == r2->rt_ifp && 642 #ifdef HAVE_ROUTE_METRIC 643 r1->rt_metric == r2->rt_metric && 644 #endif 645 sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0); 646 } 647 648 static bool 649 rt_doroute(rb_tree_t *kroutes, struct rt *rt) 650 { 651 struct dhcpcd_ctx *ctx; 652 struct rt *or; 653 654 ctx = rt->rt_ifp->ctx; 655 /* Do we already manage it? */ 656 or = rb_tree_find_node(&ctx->routes, rt); 657 if (or != NULL) { 658 if (rt->rt_dflags & RTDF_FAKE) 659 return true; 660 if (or->rt_dflags & RTDF_FAKE || 661 !rt_cmp(rt, or) || 662 (rt->rt_ifa.sa_family != AF_UNSPEC && 663 sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) || 664 or->rt_mtu != rt->rt_mtu) 665 { 666 if (!rt_add(kroutes, rt, or)) 667 return false; 668 } 669 rb_tree_remove_node(&ctx->routes, or); 670 rt_free(or); 671 } else { 672 if (rt->rt_dflags & RTDF_FAKE) { 673 or = rb_tree_find_node(kroutes, rt); 674 if (or == NULL) 675 return false; 676 if (!rt_cmp(rt, or)) 677 return false; 678 } else { 679 if (!rt_add(kroutes, rt, NULL)) 680 return false; 681 } 682 } 683 684 return true; 685 } 686 687 void 688 rt_build(struct dhcpcd_ctx *ctx, int af) 689 { 690 rb_tree_t routes, added, kroutes; 691 struct rt *rt, *rtn; 692 unsigned long long o; 693 694 rb_tree_init(&routes, &rt_compare_proto_ops); 695 rb_tree_init(&added, &rt_compare_os_ops); 696 rb_tree_init(&kroutes, &rt_compare_os_ops); 697 if_initrt(ctx, &kroutes, af); 698 ctx->rt_order = 0; 699 ctx->options |= DHCPCD_RTBUILD; 700 701 #ifdef INET 702 if (!inet_getroutes(ctx, &routes)) 703 goto getfail; 704 #endif 705 #ifdef INET6 706 if (!inet6_getroutes(ctx, &routes)) 707 goto getfail; 708 #endif 709 710 #ifdef BSD 711 /* Rewind the miss filter */ 712 ctx->rt_missfilterlen = 0; 713 #endif 714 715 RB_TREE_FOREACH_SAFE(rt, &routes, rtn) { 716 if (rt->rt_ifp->active) { 717 if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE)) 718 continue; 719 } else if (!(ctx->options & DHCPCD_CONFIGURE)) 720 continue; 721 #ifdef BSD 722 if (rt_is_default(rt) && 723 if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1) 724 logerr("if_missfilter"); 725 #endif 726 if ((rt->rt_dest.sa_family != af && 727 rt->rt_dest.sa_family != AF_UNSPEC) || 728 (rt->rt_gateway.sa_family != af && 729 rt->rt_gateway.sa_family != AF_UNSPEC)) 730 continue; 731 /* Is this route already in our table? */ 732 if (rb_tree_find_node(&added, rt) != NULL) 733 continue; 734 if (rt_doroute(&kroutes, rt)) { 735 rb_tree_remove_node(&routes, rt); 736 if (rb_tree_insert_node(&added, rt) != rt) { 737 errno = EEXIST; 738 logerr(__func__); 739 rt_free(rt); 740 } 741 } 742 } 743 744 #ifdef BSD 745 if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP) 746 logerr("if_missfilter_apply"); 747 #endif 748 749 /* Remove old routes we used to manage. */ 750 RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) { 751 if ((rt->rt_dest.sa_family != af && 752 rt->rt_dest.sa_family != AF_UNSPEC) || 753 (rt->rt_gateway.sa_family != af && 754 rt->rt_gateway.sa_family != AF_UNSPEC)) 755 continue; 756 rb_tree_remove_node(&ctx->routes, rt); 757 if (rb_tree_find_node(&added, rt) == NULL) { 758 o = rt->rt_ifp->options ? 759 rt->rt_ifp->options->options : 760 ctx->options; 761 if ((o & 762 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != 763 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) 764 rt_delete(rt); 765 } 766 rt_free(rt); 767 } 768 769 /* XXX This needs to be optimised. */ 770 while ((rt = RB_TREE_MIN(&added)) != NULL) { 771 rb_tree_remove_node(&added, rt); 772 if (rb_tree_insert_node(&ctx->routes, rt) != rt) { 773 errno = EEXIST; 774 logerr(__func__); 775 rt_free(rt); 776 } 777 } 778 779 getfail: 780 rt_headclear(&routes, AF_UNSPEC); 781 rt_headclear(&kroutes, AF_UNSPEC); 782 } 783