1 /* 2 * dhcpcd - route management 3 * Copyright (c) 2006-2018 Roy Marples <roy@marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <assert.h> 29 #include <ctype.h> 30 #include <errno.h> 31 #include <stdbool.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "config.h" 37 #include "common.h" 38 #include "dhcpcd.h" 39 #include "if.h" 40 #include "ipv4.h" 41 #include "ipv4ll.h" 42 #include "ipv6.h" 43 #include "logerr.h" 44 #include "route.h" 45 #include "sa.h" 46 47 /* 48 * On some systems, host routes have no need for a netmask. 49 * However DHCP specifies host routes using an all-ones netmask. 50 * This handy function allows easy comparison when the two 51 * differ. 52 */ 53 static int 54 rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2) 55 { 56 57 if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST) 58 return 0; 59 return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask); 60 } 61 62 void 63 rt_init(struct dhcpcd_ctx *ctx) 64 { 65 66 TAILQ_INIT(&ctx->routes); 67 TAILQ_INIT(&ctx->kroutes); 68 TAILQ_INIT(&ctx->froutes); 69 } 70 71 static void 72 rt_desc(const char *cmd, const struct rt *rt) 73 { 74 char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN]; 75 int prefix; 76 const char *ifname; 77 bool gateway_unspec; 78 79 assert(cmd != NULL); 80 assert(rt != NULL); 81 82 sa_addrtop(&rt->rt_dest, dest, sizeof(dest)); 83 prefix = sa_toprefix(&rt->rt_netmask); 84 sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway)); 85 gateway_unspec = sa_is_unspecified(&rt->rt_gateway); 86 ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name; 87 88 if (rt->rt_flags & RTF_HOST) { 89 if (gateway_unspec) 90 loginfox("%s: %s host route to %s", 91 ifname, cmd, dest); 92 else 93 loginfox("%s: %s host route to %s via %s", 94 ifname, cmd, dest, gateway); 95 } else if (sa_is_unspecified(&rt->rt_dest) && 96 sa_is_unspecified(&rt->rt_netmask)) 97 { 98 if (gateway_unspec) 99 loginfox("%s: %s default route", 100 ifname, cmd); 101 else 102 loginfox("%s: %s default route via %s", 103 ifname, cmd, gateway); 104 } else if (gateway_unspec) 105 loginfox("%s: %s%s route to %s/%d", 106 ifname, cmd, 107 rt->rt_flags & RTF_REJECT ? " reject" : "", 108 dest, prefix); 109 else 110 loginfox("%s: %s%s route to %s/%d via %s", 111 ifname, cmd, 112 rt->rt_flags & RTF_REJECT ? " reject" : "", 113 dest, prefix, gateway); 114 } 115 116 void 117 rt_headclear0(struct dhcpcd_ctx *ctx, struct rt_head *rts, int af) 118 { 119 struct rt *rt, *rtn; 120 121 if (rts == NULL) 122 return; 123 assert(ctx != NULL); 124 assert(&ctx->froutes != rts); 125 126 TAILQ_FOREACH_SAFE(rt, rts, rt_next, rtn) { 127 if (af != AF_UNSPEC && 128 rt->rt_dest.sa_family != af && 129 rt->rt_gateway.sa_family != af) 130 continue; 131 TAILQ_REMOVE(rts, rt, rt_next); 132 TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next); 133 } 134 } 135 136 void 137 rt_headclear(struct rt_head *rts, int af) 138 { 139 struct rt *rt; 140 141 if (rts == NULL || (rt = TAILQ_FIRST(rts)) == NULL) 142 return; 143 rt_headclear0(rt->rt_ifp->ctx, rts, af); 144 } 145 146 static void 147 rt_headfree(struct rt_head *rts) 148 { 149 struct rt *rt; 150 151 while ((rt = TAILQ_FIRST(rts))) { 152 TAILQ_REMOVE(rts, rt, rt_next); 153 free(rt); 154 } 155 } 156 157 void 158 rt_dispose(struct dhcpcd_ctx *ctx) 159 { 160 161 assert(ctx != NULL); 162 rt_headfree(&ctx->routes); 163 rt_headfree(&ctx->kroutes); 164 rt_headfree(&ctx->froutes); 165 } 166 167 struct rt * 168 rt_new0(struct dhcpcd_ctx *ctx) 169 { 170 struct rt *rt; 171 172 assert(ctx != NULL); 173 if ((rt = TAILQ_FIRST(&ctx->froutes)) != NULL) 174 TAILQ_REMOVE(&ctx->froutes, rt, rt_next); 175 else if ((rt = malloc(sizeof(*rt))) == NULL) { 176 logerr(__func__); 177 return NULL; 178 } 179 memset(rt, 0, sizeof(*rt)); 180 return rt; 181 } 182 183 void 184 rt_setif(struct rt *rt, struct interface *ifp) 185 { 186 187 assert(rt != NULL); 188 assert(ifp != NULL); 189 rt->rt_ifp = ifp; 190 #ifdef HAVE_ROUTE_METRIC 191 rt->rt_metric = ifp->metric; 192 #endif 193 } 194 195 struct rt * 196 rt_new(struct interface *ifp) 197 { 198 struct rt *rt; 199 200 assert(ifp != NULL); 201 if ((rt = rt_new0(ifp->ctx)) == NULL) 202 return NULL; 203 rt_setif(rt, ifp); 204 return rt; 205 } 206 207 void 208 rt_free(struct rt *rt) 209 { 210 211 assert(rt != NULL); 212 assert(rt->rt_ifp->ctx != NULL); 213 TAILQ_INSERT_TAIL(&rt->rt_ifp->ctx->froutes, rt, rt_next); 214 } 215 216 void 217 rt_freeif(struct interface *ifp) 218 { 219 struct dhcpcd_ctx *ctx; 220 struct rt *rt, *rtn; 221 222 if (ifp == NULL) 223 return; 224 ctx = ifp->ctx; 225 TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) { 226 if (rt->rt_ifp == ifp) { 227 TAILQ_REMOVE(&ctx->routes, rt, rt_next); 228 rt_free(rt); 229 } 230 } 231 TAILQ_FOREACH_SAFE(rt, &ctx->kroutes, rt_next, rtn) { 232 if (rt->rt_ifp == ifp) { 233 TAILQ_REMOVE(&ctx->kroutes, rt, rt_next); 234 rt_free(rt); 235 } 236 } 237 } 238 239 struct rt * 240 rt_find(struct rt_head *rts, const struct rt *f) 241 { 242 struct rt *rt; 243 244 assert(rts != NULL); 245 assert(f != NULL); 246 TAILQ_FOREACH(rt, rts, rt_next) { 247 if (sa_cmp(&rt->rt_dest, &f->rt_dest) == 0 && 248 #ifdef HAVE_ROUTE_METRIC 249 (f->rt_ifp == NULL || 250 rt->rt_ifp->metric == f->rt_ifp->metric) && 251 #endif 252 rt_cmp_netmask(f, rt) == 0) 253 return rt; 254 } 255 return NULL; 256 } 257 258 static void 259 rt_kfree(struct rt *rt) 260 { 261 struct dhcpcd_ctx *ctx; 262 struct rt *f; 263 264 assert(rt != NULL); 265 ctx = rt->rt_ifp->ctx; 266 if ((f = rt_find(&ctx->kroutes, rt)) != NULL) { 267 TAILQ_REMOVE(&ctx->kroutes, f, rt_next); 268 rt_free(f); 269 } 270 } 271 272 /* If something other than dhcpcd removes a route, 273 * we need to remove it from our internal table. */ 274 void 275 rt_recvrt(int cmd, const struct rt *rt) 276 { 277 struct dhcpcd_ctx *ctx; 278 struct rt *f; 279 280 assert(rt != NULL); 281 ctx = rt->rt_ifp->ctx; 282 f = rt_find(&ctx->kroutes, rt); 283 284 switch(cmd) { 285 case RTM_DELETE: 286 if (f != NULL) { 287 TAILQ_REMOVE(&ctx->kroutes, f, rt_next); 288 rt_free(f); 289 } 290 if ((f = rt_find(&ctx->routes, rt)) != NULL) { 291 TAILQ_REMOVE(&ctx->routes, f, rt_next); 292 rt_desc("deleted", f); 293 rt_free(f); 294 } 295 break; 296 case RTM_ADD: 297 if (f != NULL) 298 break; 299 if ((f = rt_new(rt->rt_ifp)) == NULL) 300 break; 301 memcpy(f, rt, sizeof(*f)); 302 TAILQ_INSERT_TAIL(&ctx->kroutes, f, rt_next); 303 break; 304 } 305 306 #if defined(INET) && defined(HAVE_ROUTE_METRIC) 307 if (rt->rt_dest.sa_family == AF_INET) 308 ipv4ll_recvrt(cmd, rt); 309 #endif 310 } 311 312 static bool 313 rt_add(struct rt *nrt, struct rt *ort) 314 { 315 struct dhcpcd_ctx *ctx; 316 bool change; 317 318 assert(nrt != NULL); 319 ctx = nrt->rt_ifp->ctx; 320 321 /* 322 * Don't install a gateway if not asked to. 323 * This option is mainly for VPN users who want their VPN to be the 324 * default route. 325 * Because VPN's generally don't care about route management 326 * beyond their own, a longer term solution would be to remove this 327 * and get the VPN to inject the default route into dhcpcd somehow. 328 */ 329 if (((nrt->rt_ifp->active && 330 !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) || 331 (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) && 332 sa_is_unspecified(&nrt->rt_dest) && 333 sa_is_unspecified(&nrt->rt_netmask)) 334 return false; 335 336 rt_desc(ort == NULL ? "adding" : "changing", nrt); 337 338 change = false; 339 if (ort == NULL) { 340 ort = rt_find(&ctx->kroutes, nrt); 341 if (ort != NULL && 342 ((ort->rt_flags & RTF_REJECT && 343 nrt->rt_flags & RTF_REJECT) || 344 (ort->rt_ifp == nrt->rt_ifp && 345 #ifdef HAVE_ROUTE_METRIC 346 ort->rt_metric == nrt->rt_metric && 347 #endif 348 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0))) 349 { 350 if (ort->rt_mtu == nrt->rt_mtu) 351 return true; 352 change = true; 353 } 354 } else if (ort->rt_dflags & RTDF_FAKE && 355 !(nrt->rt_dflags & RTDF_FAKE) && 356 ort->rt_ifp == nrt->rt_ifp && 357 #ifdef HAVE_ROUTE_METRIC 358 ort->rt_metric == nrt->rt_metric && 359 #endif 360 sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 && 361 rt_cmp_netmask(ort, nrt) == 0 && 362 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0) 363 { 364 if (ort->rt_mtu == nrt->rt_mtu) 365 return true; 366 change = true; 367 } 368 369 #ifdef RTF_CLONING 370 /* BSD can set routes to be cloning routes. 371 * Cloned routes inherit the parent flags. 372 * As such, we need to delete and re-add the route to flush children 373 * to correct the flags. */ 374 if (change && ort != NULL && ort->rt_flags & RTF_CLONING) 375 change = false; 376 #endif 377 378 if (change) { 379 if (if_route(RTM_CHANGE, nrt) != -1) 380 return true; 381 if (errno != ESRCH) 382 logerr("if_route (CHG)"); 383 } 384 385 #ifdef HAVE_ROUTE_METRIC 386 /* With route metrics, we can safely add the new route before 387 * deleting the old route. */ 388 if (if_route(RTM_ADD, nrt) != -1) { 389 if (ort != NULL) { 390 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH) 391 logerr("if_route (DEL)"); 392 rt_kfree(ort); 393 } 394 return true; 395 } 396 397 /* If the kernel claims the route exists we need to rip out the 398 * old one first. */ 399 if (errno != EEXIST || ort == NULL) 400 goto logerr; 401 #endif 402 403 /* No route metrics, we need to delete the old route before 404 * adding the new one. */ 405 #ifdef ROUTE_PER_GATEWAY 406 errno = 0; 407 #endif 408 if (ort != NULL) { 409 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH) 410 logerr("if_route (DEL)"); 411 else 412 rt_kfree(ort); 413 } 414 #ifdef ROUTE_PER_GATEWAY 415 /* The OS allows many routes to the same dest with different gateways. 416 * dhcpcd does not support this yet, so for the time being just keep on 417 * deleting the route until there is an error. */ 418 if (ort != NULL && errno == 0) { 419 for (;;) { 420 if (if_route(RTM_DELETE, ort) == -1) 421 break; 422 } 423 } 424 #endif 425 if (if_route(RTM_ADD, nrt) != -1) 426 return true; 427 #ifdef HAVE_ROUTE_METRIC 428 logerr: 429 #endif 430 logerr("if_route (ADD)"); 431 return false; 432 } 433 434 static bool 435 rt_delete(struct rt *rt) 436 { 437 int retval; 438 439 rt_desc("deleting", rt); 440 retval = if_route(RTM_DELETE, rt) == -1 ? false : true; 441 if (!retval && errno != ENOENT && errno != ESRCH) 442 logerr(__func__); 443 /* Remove the route from our kernel table so we can add a 444 * IPv4LL default route if possible. */ 445 else 446 rt_kfree(rt); 447 return retval; 448 } 449 450 static bool 451 rt_cmp(const struct rt *r1, const struct rt *r2) 452 { 453 454 return (r1->rt_ifp == r2->rt_ifp && 455 #ifdef HAVE_ROUTE_METRIC 456 r1->rt_metric == r2->rt_metric && 457 #endif 458 sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0); 459 } 460 461 static bool 462 rt_doroute(struct rt *rt) 463 { 464 struct dhcpcd_ctx *ctx; 465 struct rt *or; 466 467 ctx = rt->rt_ifp->ctx; 468 /* Do we already manage it? */ 469 if ((or = rt_find(&ctx->routes, rt))) { 470 if (rt->rt_dflags & RTDF_FAKE) 471 return true; 472 if (or->rt_dflags & RTDF_FAKE || 473 !rt_cmp(rt, or) || 474 (rt->rt_ifa.sa_family != AF_UNSPEC && 475 sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) || 476 or->rt_mtu != rt->rt_mtu) 477 { 478 if (!rt_add(rt, or)) 479 return false; 480 } 481 TAILQ_REMOVE(&ctx->routes, or, rt_next); 482 rt_free(or); 483 } else { 484 if (rt->rt_dflags & RTDF_FAKE) { 485 if ((or = rt_find(&ctx->kroutes, rt)) == NULL) 486 return false; 487 if (!rt_cmp(rt, or)) 488 return false; 489 } else { 490 if (!rt_add(rt, NULL)) 491 return false; 492 } 493 } 494 495 return true; 496 } 497 498 void 499 rt_build(struct dhcpcd_ctx *ctx, int af) 500 { 501 struct rt_head routes, added; 502 struct rt *rt, *rtn; 503 unsigned long long o; 504 505 /* We need to have the interfaces in the correct order to ensure 506 * our routes are managed correctly. */ 507 if_sortinterfaces(ctx); 508 509 TAILQ_INIT(&routes); 510 TAILQ_INIT(&added); 511 512 switch (af) { 513 #ifdef INET 514 case AF_INET: 515 if (!inet_getroutes(ctx, &routes)) 516 goto getfail; 517 break; 518 #endif 519 #ifdef INET6 520 case AF_INET6: 521 if (!inet6_getroutes(ctx, &routes)) 522 goto getfail; 523 break; 524 #endif 525 } 526 527 TAILQ_FOREACH_SAFE(rt, &routes, rt_next, rtn) { 528 if (rt->rt_dest.sa_family != af && 529 rt->rt_gateway.sa_family != af) 530 continue; 531 /* Is this route already in our table? */ 532 if ((rt_find(&added, rt)) != NULL) 533 continue; 534 if (rt_doroute(rt)) { 535 TAILQ_REMOVE(&routes, rt, rt_next); 536 TAILQ_INSERT_TAIL(&added, rt, rt_next); 537 } 538 } 539 540 /* Remove old routes we used to manage. */ 541 TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) { 542 if (rt->rt_dest.sa_family != af && 543 rt->rt_gateway.sa_family != af) 544 continue; 545 TAILQ_REMOVE(&ctx->routes, rt, rt_next); 546 if (rt_find(&added, rt) == NULL) { 547 o = rt->rt_ifp->options ? 548 rt->rt_ifp->options->options : 549 ctx->options; 550 if ((o & 551 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != 552 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) 553 rt_delete(rt); 554 } 555 TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next); 556 } 557 558 rt_headclear(&ctx->routes, af); 559 TAILQ_CONCAT(&ctx->routes, &added, rt_next); 560 561 getfail: 562 rt_headclear(&routes, AF_UNSPEC); 563 } 564