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