1 /* 2 * dhcpcd - route management 3 * SPDX-License-Identifier: BSD-2-Clause 4 * Copyright (c) 2006-2025 Roy Marples <roy (at) 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 <syslog.h> 37 #include <unistd.h> 38 39 #include "config.h" // IWYU pragma: keep 40 #include "common.h" 41 #include "dhcpcd.h" 42 #include "if-options.h" 43 #include "if.h" 44 #include "ipv4.h" 45 #include "ipv4ll.h" 46 #include "ipv6.h" 47 #include "logerr.h" 48 #include "route.h" 49 #include "sa.h" 50 51 /* Needed for NetBSD-6, 7 and 8. */ 52 #ifndef RB_TREE_FOREACH_SAFE 53 #ifndef RB_TREE_PREV 54 #define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT) 55 #define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT) 56 #endif 57 #define RB_TREE_FOREACH_SAFE(N, T, S) \ 58 for ((N) = RB_TREE_MIN(T); (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); (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \ 62 (N) = (S)) 63 #endif 64 65 /* 66 * For our purposes, RTF_CONNECTED is the same as RTF_CLONING. 67 * If we change the route, we want to flush anything dynamically created. 68 */ 69 #if defined(BSD) && !defined(RTF_CLONING) && defined(RTF_CONNECTED) 70 #define RTF_CLONING RTF_CONNECTED 71 #endif 72 73 #ifdef RT_FREE_ROUTE_TABLE_STATS 74 static size_t croutes; 75 static size_t nroutes; 76 static size_t froutes; 77 static size_t mroutes; 78 #endif 79 80 static void 81 rt_maskedaddr(struct sockaddr *dst, const struct sockaddr *addr, 82 const struct sockaddr *netmask) 83 { 84 const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data; 85 char *dstp = dst->sa_data; 86 const char *addre = (char *)dst + sa_len(addr); 87 const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask)); 88 89 dst->sa_family = addr->sa_family; 90 #ifdef HAVE_SA_LEN 91 dst->sa_len = addr->sa_len; 92 #endif 93 94 if (sa_is_unspecified(netmask)) { 95 if (addre > dstp) 96 memcpy(dstp, addrp, (size_t)(addre - dstp)); 97 return; 98 } 99 100 while (dstp < netmaske) 101 *dstp++ = *addrp++ & *netmaskp++; 102 if (dstp < addre) 103 memset(dstp, 0, (size_t)(addre - dstp)); 104 } 105 106 /* 107 * On some systems, host routes have no need for a netmask. 108 * However DHCP specifies host routes using an all-ones netmask. 109 * This handy function allows easy comparison when the two 110 * differ. 111 */ 112 static int 113 rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2) 114 { 115 if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST) 116 return 0; 117 return sa_cmp(rt1->rt_netmask, rt2->rt_netmask); 118 } 119 120 int 121 rt_cmp_dest(const struct rt *rt1, const struct rt *rt2) 122 { 123 struct sockaddr_storage ss1 = { .ss_family = AF_UNSPEC }; 124 struct sockaddr_storage ss2 = { .ss_family = AF_UNSPEC }; 125 struct sockaddr *ma1 = (struct sockaddr *)&ss1; 126 struct sockaddr *ma2 = (struct sockaddr *)&ss2; 127 int c; 128 129 rt_maskedaddr(ma1, rt1->rt_dest, rt1->rt_netmask); 130 rt_maskedaddr(ma2, rt2->rt_dest, rt2->rt_netmask); 131 c = sa_cmp(ma1, ma2); 132 if (c != 0) 133 return c; 134 135 return rt_cmp_netmask(rt1, rt2); 136 } 137 138 static int 139 rt_compare_os(__unused void *context, const void *node1, const void *node2) 140 { 141 const struct rt *rt1 = node1, *rt2 = node2; 142 int c; 143 144 /* Sort by masked destination. */ 145 c = rt_cmp_dest(rt1, rt2); 146 if (c != 0) 147 return c; 148 149 #ifdef HAVE_ROUTE_METRIC 150 c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric); 151 #endif 152 return c; 153 } 154 155 static int 156 rt_compare_list(__unused void *context, const void *node1, const void *node2) 157 { 158 const struct rt *rt1 = node1, *rt2 = node2; 159 160 if (rt1->rt_order > rt2->rt_order) 161 return 1; 162 if (rt1->rt_order < rt2->rt_order) 163 return -1; 164 return 0; 165 } 166 167 static int 168 rt_compare_proto(void *context, const void *node1, const void *node2) 169 { 170 const struct rt *rt1 = node1, *rt2 = node2; 171 int c; 172 struct interface *ifp1, *ifp2; 173 174 assert(rt1->rt_ifp != NULL); 175 assert(rt2->rt_ifp != NULL); 176 ifp1 = rt1->rt_ifp; 177 ifp2 = rt2->rt_ifp; 178 179 /* Prefer interfaces with a carrier. */ 180 c = ifp1->carrier - ifp2->carrier; 181 if (c != 0) 182 return -c; 183 184 /* Prefer roaming over non roaming if both carriers are down. */ 185 if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) { 186 bool roam1 = if_roaming(ifp1); 187 bool roam2 = if_roaming(ifp2); 188 189 if (roam1 != roam2) 190 return roam1 ? 1 : -1; 191 } 192 193 #ifdef INET 194 /* IPv4LL routes always come last */ 195 if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL)) 196 return -1; 197 else if (!(rt1->rt_dflags & RTDF_IPV4LL) && 198 rt2->rt_dflags & RTDF_IPV4LL) 199 return 1; 200 #endif 201 202 /* Lower metric interfaces come first. */ 203 c = (int)(ifp1->metric - ifp2->metric); 204 if (c != 0) 205 return c; 206 207 /* Finally the order in which the route was given to us. */ 208 return rt_compare_list(context, rt1, rt2); 209 } 210 211 static const rb_tree_ops_t rt_compare_os_ops = { 212 .rbto_compare_nodes = rt_compare_os, 213 .rbto_compare_key = rt_compare_os, 214 .rbto_node_offset = offsetof(struct rt, rt_tree), 215 .rbto_context = NULL 216 }; 217 218 const rb_tree_ops_t rt_compare_list_ops = { 219 .rbto_compare_nodes = rt_compare_list, 220 .rbto_compare_key = rt_compare_list, 221 .rbto_node_offset = offsetof(struct rt, rt_tree), 222 .rbto_context = NULL 223 }; 224 225 const rb_tree_ops_t rt_compare_proto_ops = { 226 .rbto_compare_nodes = rt_compare_proto, 227 .rbto_compare_key = rt_compare_proto, 228 .rbto_node_offset = offsetof(struct rt, rt_tree), 229 .rbto_context = NULL 230 }; 231 232 #ifdef RT_FREE_ROUTE_TABLE 233 static int 234 rt_compare_free(__unused void *context, const void *node1, const void *node2) 235 { 236 return node1 == node2 ? 0 : node1 < node2 ? -1 : 1; 237 } 238 239 static const rb_tree_ops_t rt_compare_free_ops = { 240 .rbto_compare_nodes = rt_compare_free, 241 .rbto_compare_key = rt_compare_free, 242 .rbto_node_offset = offsetof(struct rt, rt_tree), 243 .rbto_context = NULL 244 }; 245 #endif 246 247 void 248 rt_init_routes(struct dhcpcd_ctx *ctx) 249 { 250 rb_tree_init(&ctx->routes, &rt_compare_os_ops); 251 #ifdef RT_FREE_ROUTE_TABLE 252 rb_tree_init(&ctx->froutes, &rt_compare_free_ops); 253 #endif 254 } 255 256 bool 257 rt_is_default(const struct rt *rt) 258 { 259 return sa_is_unspecified(rt->rt_dest) && 260 sa_is_unspecified(rt->rt_netmask); 261 } 262 263 static void 264 rt_desc(int loglevel, const char *cmd, const struct rt *rt) 265 { 266 char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN]; 267 int prefix; 268 const char *ifname; 269 bool gateway_unspec; 270 271 assert(cmd != NULL); 272 assert(rt != NULL); 273 274 sa_addrtop(rt->rt_dest, dest, sizeof(dest)); 275 prefix = sa_toprefix(rt->rt_netmask); 276 sa_addrtop(rt->rt_gateway, gateway, sizeof(gateway)); 277 gateway_unspec = sa_is_unspecified(rt->rt_gateway); 278 ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name; 279 280 if (rt->rt_flags & RTF_HOST) { 281 if (gateway_unspec) 282 logmessage(loglevel, "%s: %s host route to %s", ifname, 283 cmd, dest); 284 else 285 logmessage(loglevel, "%s: %s host route to %s via %s", 286 ifname, cmd, dest, gateway); 287 } else if (rt_is_default(rt)) { 288 if (gateway_unspec) 289 logmessage(loglevel, "%s: %s default route", ifname, 290 cmd); 291 else 292 logmessage(loglevel, "%s: %s default route via %s", 293 ifname, cmd, gateway); 294 } else if (gateway_unspec) 295 logmessage(loglevel, "%s: %s%s route to %s/%d", ifname, cmd, 296 rt->rt_flags & RTF_REJECT ? " reject" : "", dest, prefix); 297 else 298 logmessage(loglevel, "%s: %s%s route to %s/%d via %s", ifname, 299 cmd, rt->rt_flags & RTF_REJECT ? " reject" : "", dest, 300 prefix, gateway); 301 } 302 303 void 304 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af) 305 { 306 struct rt *rt, *rtn; 307 308 if (rts == NULL) 309 return; 310 #ifdef RT_FREE_ROUTE_TABLE 311 if (ctx != NULL) 312 assert(&ctx->froutes != rts); 313 #endif 314 315 RB_TREE_FOREACH_SAFE(rt, rts, rtn) 316 { 317 if (af != AF_UNSPEC && rt->rt_dest->sa_family != af && 318 rt->rt_gateway->sa_family != af) 319 continue; 320 rb_tree_remove_node(rts, rt); 321 rt_free(rt); 322 } 323 } 324 325 void 326 rt_headclear(rb_tree_t *rts, int af) 327 { 328 struct rt *rt; 329 330 if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL) 331 return; 332 rt_headclear0(rt->rt_ifp ? rt->rt_ifp->ctx : NULL, rts, af); 333 } 334 335 static void 336 rt_headfree(rb_tree_t *rts) 337 { 338 struct rt *rt; 339 340 while ((rt = RB_TREE_MIN(rts)) != NULL) { 341 rb_tree_remove_node(rts, rt); 342 free(rt); 343 } 344 } 345 346 void 347 rt_dispose(struct dhcpcd_ctx *ctx) 348 { 349 assert(ctx != NULL); 350 rt_headfree(&ctx->routes); 351 #ifdef RT_FREE_ROUTE_TABLE 352 rt_headfree(&ctx->froutes); 353 #ifdef RT_FREE_ROUTE_TABLE_STATS 354 logdebugx("free route list used %zu times", froutes); 355 logdebugx("new routes from route free list %zu", nroutes); 356 logdebugx("maximum route free list size %zu", mroutes); 357 #endif 358 #endif 359 } 360 361 static void 362 rt_setup_sa(struct rt *rt) 363 { 364 rt->rt_dest = (struct sockaddr *)&rt->rt_ss_dest; 365 rt->rt_netmask = (struct sockaddr *)&rt->rt_ss_netmask; 366 rt->rt_gateway = (struct sockaddr *)&rt->rt_ss_gateway; 367 rt->rt_ifa = (struct sockaddr *)&rt->rt_ss_ifa; 368 } 369 370 void 371 rt_init(struct rt *rt) 372 { 373 memset(rt, 0, sizeof(*rt)); 374 rt_setup_sa(rt); 375 } 376 377 void 378 rt_copy(struct rt *dst, const struct rt *src) 379 { 380 memcpy(dst, src, sizeof(*dst)); 381 rt_setup_sa(dst); 382 } 383 384 struct rt * 385 rt_new0(struct dhcpcd_ctx *ctx) 386 { 387 struct rt *rt; 388 389 assert(ctx != NULL); 390 #ifdef RT_FREE_ROUTE_TABLE 391 if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) { 392 rb_tree_remove_node(&ctx->froutes, rt); 393 #ifdef RT_FREE_ROUTE_TABLE_STATS 394 croutes--; 395 nroutes++; 396 #endif 397 } else 398 #endif 399 if ((rt = malloc(sizeof(*rt))) == NULL) { 400 logerr(__func__); 401 return NULL; 402 } 403 rt_init(rt); 404 return rt; 405 } 406 407 void 408 rt_setif(struct rt *rt, struct interface *ifp) 409 { 410 assert(rt != NULL); 411 assert(ifp != NULL); 412 rt->rt_ifp = ifp; 413 #ifdef HAVE_ROUTE_METRIC 414 rt->rt_metric = ifp->metric; 415 if (if_roaming(ifp)) 416 rt->rt_metric += RTMETRIC_ROAM; 417 #endif 418 } 419 420 struct rt * 421 rt_new(struct interface *ifp) 422 { 423 struct rt *rt; 424 425 assert(ifp != NULL); 426 if ((rt = rt_new0(ifp->ctx)) == NULL) 427 return NULL; 428 rt_setif(rt, ifp); 429 return rt; 430 } 431 432 struct rt * 433 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx) 434 { 435 rt->rt_order = ctx->rt_order++; 436 if (rb_tree_insert_node(tree, rt) == rt) 437 return rt; 438 439 rt_free(rt); 440 errno = EEXIST; 441 return NULL; 442 } 443 444 struct rt * 445 rt_proto_add(rb_tree_t *tree, struct rt *rt) 446 { 447 assert(rt->rt_ifp != NULL); 448 return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx); 449 } 450 451 void 452 rt_free(struct rt *rt) 453 { 454 #ifdef RT_FREE_ROUTE_TABLE 455 struct dhcpcd_ctx *ctx; 456 457 assert(rt != NULL); 458 if (rt->rt_ifp == NULL) { 459 free(rt); 460 return; 461 } 462 463 ctx = rt->rt_ifp->ctx; 464 rb_tree_insert_node(&ctx->froutes, rt); 465 #ifdef RT_FREE_ROUTE_TABLE_STATS 466 croutes++; 467 froutes++; 468 if (croutes > mroutes) 469 mroutes = croutes; 470 #endif 471 #else 472 free(rt); 473 #endif 474 } 475 476 void 477 rt_freeif(struct interface *ifp) 478 { 479 struct dhcpcd_ctx *ctx; 480 struct rt *rt, *rtn; 481 482 if (ifp == NULL) 483 return; 484 ctx = ifp->ctx; 485 RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) 486 { 487 if (rt->rt_ifp == ifp) { 488 rb_tree_remove_node(&ctx->routes, rt); 489 rt_free(rt); 490 } 491 } 492 } 493 494 /* If something other than dhcpcd removes a route, 495 * we need to remove it from our internal table. */ 496 void 497 rt_recvrt(int cmd, const struct rt *rt, pid_t pid) 498 { 499 struct dhcpcd_ctx *ctx; 500 struct rt *f; 501 502 assert(rt != NULL); 503 assert(rt->rt_ifp != NULL); 504 assert(rt->rt_ifp->ctx != NULL); 505 506 ctx = rt->rt_ifp->ctx; 507 508 switch (cmd) { 509 case RTM_DELETE: 510 f = rb_tree_find_node(&ctx->routes, rt); 511 if (f != NULL) { 512 char buf[32]; 513 514 rb_tree_remove_node(&ctx->routes, f); 515 snprintf(buf, sizeof(buf), "pid %d deleted", (int)pid); 516 rt_desc(LOG_WARNING, buf, f); 517 rt_free(f); 518 } 519 break; 520 } 521 522 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC) 523 if (rt->rt_dest->sa_family == AF_INET) 524 ipv4ll_recvrt(cmd, rt); 525 #endif 526 } 527 528 /* Compare miscellaneous route details */ 529 static int 530 rt_cmp_mtu(struct rt *nrt, struct rt *ort) 531 { 532 #if defined(__FreeBSD__) || defined(__DragonFly__) 533 /* FreeBSD puts the interface MTU into the route MTU 534 * if the route does not define it's own. */ 535 unsigned int nmtu, omtu; 536 537 nmtu = nrt->rt_mtu ? nrt->rt_mtu : (unsigned int)nrt->rt_ifp->mtu; 538 omtu = ort->rt_mtu ? ort->rt_mtu : (unsigned int)ort->rt_ifp->mtu; 539 if (omtu != nmtu) 540 return 1; 541 #else 542 if (ort->rt_mtu != nrt->rt_mtu) 543 return 1; 544 #endif 545 546 return 0; 547 } 548 549 #ifdef HAVE_ROUTE_LIFETIME 550 static int 551 rt_cmp_lifetime(struct rt *nrt, struct rt *ort) 552 { 553 /* There might be a minor difference between kernel route 554 * lifetime and our lifetime due to processing times. 555 * We allow a small deviation to avoid needless route changes. 556 * dhcpcd will expire the route regardless of route lifetime support. 557 */ 558 struct timespec ts; 559 uint32_t deviation; 560 561 timespecsub(&nrt->rt_acquired, &ort->rt_acquired, &ts); 562 if (ts.tv_sec < 0) 563 ts.tv_sec = -ts.tv_sec; 564 if (ts.tv_sec > RTLIFETIME_DEV_MAX) 565 return 1; 566 if (nrt->rt_lifetime > ort->rt_lifetime) 567 deviation = nrt->rt_lifetime - ort->rt_lifetime; 568 else 569 deviation = ort->rt_lifetime - nrt->rt_lifetime; 570 if (deviation > RTLIFETIME_DEV_MAX) 571 return 1; 572 573 return 0; 574 } 575 #endif 576 577 static bool 578 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort) 579 { 580 struct dhcpcd_ctx *ctx; 581 struct rt *krt; 582 int loglevel = LOG_INFO; 583 bool change, result = false; 584 585 assert(nrt != NULL); 586 ctx = nrt->rt_ifp->ctx; 587 588 /* 589 * Don't install a gateway if not asked to. 590 * This option is mainly for VPN users who want their VPN to be the 591 * default route. 592 * Because VPN's generally don't care about route management 593 * beyond their own, a longer term solution would be to remove this 594 * and get the VPN to inject the default route into dhcpcd somehow. 595 */ 596 if (((nrt->rt_ifp->active && 597 !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) || 598 (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) && 599 sa_is_unspecified(nrt->rt_dest) && 600 sa_is_unspecified(nrt->rt_netmask)) 601 return false; 602 603 krt = rb_tree_find_node(kroutes, nrt); 604 if (krt != NULL && krt->rt_ifp == nrt->rt_ifp && 605 /* Only test flags dhcpcd controls */ 606 (krt->rt_flags & (RTF_HOST | RTF_REJECT)) == nrt->rt_flags && 607 #ifdef HAVE_ROUTE_METRIC 608 krt->rt_metric == nrt->rt_metric && 609 #endif 610 sa_cmp(krt->rt_dest, nrt->rt_dest) == 0 && 611 rt_cmp_netmask(krt, nrt) == 0 && 612 sa_cmp(krt->rt_gateway, nrt->rt_gateway) == 0 && 613 (nrt->rt_ifp->flags & IFF_LOOPBACK || rt_cmp_mtu(krt, nrt) == 0)) { 614 #ifdef HAVE_ROUTE_LIFETIME 615 if (rt_cmp_lifetime(krt, nrt) == 0) { 616 rt_desc(LOG_DEBUG, "keeping", krt); 617 return true; 618 } else 619 loglevel = LOG_DEBUG; 620 #else 621 rt_desc(LOG_DEBUG, "keeping", krt); 622 return true; 623 #endif 624 } 625 626 rt_desc(loglevel, ort == NULL ? "adding" : "changing", nrt); 627 628 change = krt != NULL; 629 #ifdef RTF_CLONING 630 /* BSD can set routes to be cloning routes. 631 * Cloned routes inherit the parent flags. 632 * As such, we need to delete and re-add the route to flush children 633 * to correct the flags. */ 634 if (change && krt != NULL && krt->rt_flags & RTF_CLONING) 635 change = false; 636 #endif 637 /* Reject routes have a gateway, non reject routes don't. 638 * BSD kernels at least preserve RTF_GATEWAY so we need to punt it. */ 639 if (change && krt->rt_flags & RTF_REJECT && 640 !(nrt->rt_flags & RTF_REJECT)) 641 change = false; 642 643 if (change) { 644 if (if_route(RTM_CHANGE, nrt) != -1) { 645 result = true; 646 goto out; 647 } 648 if (errno != ESRCH) 649 logerr("if_route (CHG)"); 650 } 651 652 #ifdef HAVE_ROUTE_METRIC 653 /* With route metrics, we can safely add the new route before 654 * deleting the old route. */ 655 if (if_route(RTM_ADD, nrt) != -1) { 656 if (krt != NULL) { 657 if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH) 658 logerr("if_route (DEL)"); 659 } 660 result = true; 661 goto out; 662 } 663 664 /* If the kernel claims the route exists we need to rip out the 665 * old one first. */ 666 if (errno != EEXIST || ort == NULL) 667 goto logerr; 668 #endif 669 670 /* No route metrics, we need to delete the old route before 671 * adding the new one. */ 672 #ifdef ROUTE_PER_GATEWAY 673 errno = 0; 674 #endif 675 if (krt != NULL) { 676 if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH) 677 logerr("if_route (DEL)"); 678 } 679 #ifdef ROUTE_PER_GATEWAY 680 /* The OS allows many routes to the same dest with different gateways. 681 * dhcpcd does not support this yet, so for the time being just keep on 682 * deleting the route until there is an error. */ 683 if (krt != NULL && errno == 0) { 684 for (;;) { 685 if (if_route(RTM_DELETE, krt) == -1) 686 break; 687 } 688 } 689 #endif 690 691 /* Shouldn't need to check for EEXIST, but some kernels don't 692 * dump the subnet route just after we added the address. */ 693 if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) { 694 result = true; 695 goto out; 696 } 697 698 #ifdef HAVE_ROUTE_METRIC 699 logerr: 700 #endif 701 logerr("if_route (ADD)"); 702 703 out: 704 if (krt != NULL) { 705 rb_tree_remove_node(kroutes, krt); 706 rt_free(krt); 707 } 708 return result; 709 } 710 711 static bool 712 rt_delete(struct rt *rt) 713 { 714 int retval; 715 716 rt_desc(LOG_INFO, "deleting", rt); 717 retval = if_route(RTM_DELETE, rt) == -1 ? false : true; 718 if (!retval && errno != ENOENT && errno != ESRCH) 719 logerr(__func__); 720 return retval; 721 } 722 723 static int 724 rt_cmp(const struct rt *r1, const struct rt *r2) 725 { 726 if (r1->rt_ifp == r2->rt_ifp && 727 #ifdef HAVE_ROUTE_METRIC 728 r1->rt_metric == r2->rt_metric && 729 #endif 730 sa_cmp(r1->rt_gateway, r2->rt_gateway) == 0) 731 return 0; 732 return 1; 733 } 734 735 static bool 736 rt_doroute(rb_tree_t *kroutes, struct rt *rt) 737 { 738 struct dhcpcd_ctx *ctx; 739 struct rt * or ; 740 741 ctx = rt->rt_ifp->ctx; 742 /* Do we already manage it? */ 743 or = rb_tree_find_node(&ctx->routes, rt); 744 if (or != NULL) { 745 if (rt->rt_dflags & RTDF_FAKE) 746 return true; 747 if (or->rt_dflags & RTDF_FAKE || rt_cmp(rt, or) != 0 || 748 (rt->rt_ifa->sa_family != AF_UNSPEC && 749 sa_cmp(or->rt_ifa, rt->rt_ifa) != 0) || 750 #ifdef HAVE_ROUTE_LIFETIME 751 rt_cmp_lifetime(rt, or) != 0 || 752 #endif 753 rt_cmp_mtu(rt, or) != 0) { 754 if (!rt_add(kroutes, rt, or)) 755 return false; 756 } else { 757 #ifdef HAVE_ROUTE_LIFETIME 758 /* The existing kernel route matches what we want 759 * and the lifetime is inside the allowed deviation. 760 * Persist the original acquisition time so the 761 * deviaton can drop outside what is allowed and the 762 * kernel route is re-added with a new lifetime. */ 763 rt->rt_acquired = or->rt_acquired; 764 #endif 765 } 766 767 rb_tree_remove_node(&ctx->routes, or); 768 rt_free(or); 769 } else { 770 if (rt->rt_dflags & RTDF_FAKE) { 771 or = rb_tree_find_node(kroutes, rt); 772 if (or == NULL) 773 return false; 774 if (rt_cmp(rt, or) == 0) 775 return false; 776 } else { 777 if (!rt_add(kroutes, rt, NULL)) 778 return false; 779 } 780 } 781 782 return true; 783 } 784 785 void 786 rt_build(struct dhcpcd_ctx *ctx, int af) 787 { 788 rb_tree_t routes, added, kroutes; 789 struct rt *rt, *rtn; 790 unsigned long long o; 791 792 /* When exiting with persistence, don't change any routing 793 * which maybe affected by interfaces stopping. */ 794 if ((ctx->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) == 795 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) 796 return; 797 798 rb_tree_init(&routes, &rt_compare_proto_ops); 799 rb_tree_init(&added, &rt_compare_os_ops); 800 rb_tree_init(&kroutes, &rt_compare_os_ops); 801 if (if_initrt(ctx, &kroutes, af) != 0) 802 logerr("%s: if_initrt", __func__); 803 ctx->rt_order = 0; 804 ctx->options |= DHCPCD_RTBUILD; 805 806 #ifdef INET 807 if (!inet_getroutes(ctx, &routes)) 808 goto getfail; 809 #endif 810 #ifdef INET6 811 if (!inet6_getroutes(ctx, &routes)) 812 goto getfail; 813 #endif 814 815 #ifdef HAVE_RT_MISSFILTER 816 /* Rewind the miss filter */ 817 ctx->rt_missfilterlen = 0; 818 #endif 819 820 RB_TREE_FOREACH_SAFE(rt, &routes, rtn) 821 { 822 if (rt->rt_ifp->active) { 823 if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE)) 824 continue; 825 } else if (!(ctx->options & DHCPCD_CONFIGURE)) 826 continue; 827 #ifdef HAVE_RT_MISSFILTER 828 if (rt_is_default(rt) && 829 if_missfilter(rt->rt_ifp, rt->rt_gateway) == -1) 830 logerr("if_missfilter"); 831 #endif 832 if ((rt->rt_dest->sa_family != af && 833 rt->rt_dest->sa_family != AF_UNSPEC) || 834 (rt->rt_gateway->sa_family != af && 835 rt->rt_gateway->sa_family != AF_UNSPEC)) 836 continue; 837 /* Is this route already in our table? */ 838 if (rb_tree_find_node(&added, rt) != NULL) 839 continue; 840 if (rt_doroute(&kroutes, rt)) { 841 rb_tree_remove_node(&routes, rt); 842 if (rb_tree_insert_node(&added, rt) != rt) { 843 errno = EEXIST; 844 logerr(__func__); 845 rt_free(rt); 846 } 847 } 848 } 849 850 #ifdef HAVE_RT_MISSFILTER 851 if (!(ctx->options & DHCPCD_EXITING) && 852 if_missfilter_apply(ctx) == -1 && errno != ENOTSUP) 853 logerr("if_missfilter_apply"); 854 #endif 855 856 /* Remove old routes we used to manage. */ 857 RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) 858 { 859 if ((rt->rt_dest->sa_family != af && 860 rt->rt_dest->sa_family != AF_UNSPEC) || 861 (rt->rt_gateway->sa_family != af && 862 rt->rt_gateway->sa_family != AF_UNSPEC)) 863 continue; 864 rb_tree_remove_node(&ctx->routes, rt); 865 if (rb_tree_find_node(&added, rt) == NULL) { 866 o = rt->rt_ifp->options ? rt->rt_ifp->options->options : 867 ctx->options; 868 if ((o & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != 869 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) 870 rt_delete(rt); 871 } 872 rt_free(rt); 873 } 874 875 /* XXX This needs to be optimised. */ 876 while ((rt = RB_TREE_MIN(&added)) != NULL) { 877 rb_tree_remove_node(&added, rt); 878 if (rb_tree_insert_node(&ctx->routes, rt) != rt) { 879 errno = EEXIST; 880 logerr(__func__); 881 rt_free(rt); 882 } 883 } 884 885 getfail: 886 rt_headclear(&routes, AF_UNSPEC); 887 rt_headclear(&kroutes, AF_UNSPEC); 888 } 889