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