1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * dhcpcd - route management 4 * Copyright (c) 2006-2019 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 /* 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 180 static 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 187 const 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 194 const 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 202 static int 203 rt_compare_free(__unused void *context, const void *node1, const void *node2) 204 { 205 206 return node1 == node2 ? 0 : node1 < node2 ? -1 : 1; 207 } 208 209 static 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 217 void 218 rt_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 227 bool 228 rt_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 235 static void 236 rt_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 278 void 279 rt_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 300 void 301 rt_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 310 static void 311 rt_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 321 void 322 rt_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 337 struct rt * 338 rt_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 360 void 361 rt_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 372 struct rt * 373 rt_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 384 struct rt * 385 rt_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 396 struct rt * 397 rt_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 404 void 405 rt_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 429 void 430 rt_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. */ 448 void 449 rt_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 480 static bool 481 rt_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 605 logerr: 606 #endif 607 logerr("if_route (ADD)"); 608 609 out: 610 if (kroute) { 611 rb_tree_remove_node(kroutes, ort); 612 rt_free(ort); 613 } 614 return result; 615 } 616 617 static bool 618 rt_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 629 static bool 630 rt_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 640 static bool 641 rt_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 679 void 680 rt_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 switch (af) { 694 #ifdef INET 695 case AF_INET: 696 if (!inet_getroutes(ctx, &routes)) 697 goto getfail; 698 break; 699 #endif 700 #ifdef INET6 701 case AF_INET6: 702 if (!inet6_getroutes(ctx, &routes)) 703 goto getfail; 704 break; 705 #endif 706 } 707 708 RB_TREE_FOREACH_SAFE(rt, &routes, rtn) { 709 if ((rt->rt_dest.sa_family != af && 710 rt->rt_dest.sa_family != AF_UNSPEC) || 711 (rt->rt_gateway.sa_family != af && 712 rt->rt_gateway.sa_family != AF_UNSPEC)) 713 continue; 714 /* Is this route already in our table? */ 715 if (rb_tree_find_node(&added, rt) != NULL) 716 continue; 717 if (rt_doroute(&kroutes, rt)) { 718 rb_tree_remove_node(&routes, rt); 719 if (rb_tree_insert_node(&added, rt) != rt) { 720 errno = EEXIST; 721 logerr(__func__); 722 rt_free(rt); 723 } 724 } 725 } 726 727 /* Remove old routes we used to manage. */ 728 RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) { 729 if ((rt->rt_dest.sa_family != af && 730 rt->rt_dest.sa_family != AF_UNSPEC) || 731 (rt->rt_gateway.sa_family != af && 732 rt->rt_gateway.sa_family != AF_UNSPEC)) 733 continue; 734 rb_tree_remove_node(&ctx->routes, rt); 735 if (rb_tree_find_node(&added, rt) == NULL) { 736 o = rt->rt_ifp->options ? 737 rt->rt_ifp->options->options : 738 ctx->options; 739 if ((o & 740 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != 741 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) 742 rt_delete(rt); 743 } 744 rt_free(rt); 745 } 746 747 /* XXX This needs to be optimised. */ 748 while ((rt = RB_TREE_MIN(&added)) != NULL) { 749 rb_tree_remove_node(&added, rt); 750 if (rb_tree_insert_node(&ctx->routes, rt) != rt) { 751 errno = EEXIST; 752 logerr(__func__); 753 rt_free(rt); 754 } 755 } 756 757 758 getfail: 759 rt_headclear(&routes, AF_UNSPEC); 760 rt_headclear(&kroutes, AF_UNSPEC); 761 } 762