1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2020 Alexander V. Chernikov 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 <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 #include "opt_inet.h" 31 #include "opt_inet6.h" 32 #include "opt_route.h" 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/lock.h> 37 #include <sys/rwlock.h> 38 #include <sys/malloc.h> 39 #include <sys/socket.h> 40 #include <sys/sysctl.h> 41 #include <sys/kernel.h> 42 #include <sys/epoch.h> 43 44 #include <net/if.h> 45 #include <net/if_var.h> 46 #include <net/if_dl.h> 47 #include <net/route.h> 48 #include <net/route/route_ctl.h> 49 #include <net/route/route_var.h> 50 #include <net/route/nhop_utils.h> 51 #include <net/route/nhop.h> 52 #include <net/route/nhop_var.h> 53 #include <net/vnet.h> 54 55 #define DEBUG_MOD_NAME nhop_ctl 56 #define DEBUG_MAX_LEVEL LOG_DEBUG 57 #include <net/route/route_debug.h> 58 _DECLARE_DEBUG(LOG_INFO); 59 60 /* 61 * This file contains core functionality for the nexthop ("nhop") route subsystem. 62 * The business logic needed to create nexhop objects is implemented here. 63 * 64 * Nexthops in the original sense are the objects containing all the necessary 65 * information to forward the packet to the selected destination. 66 * In particular, nexthop is defined by a combination of 67 * ifp, ifa, aifp, mtu, gw addr(if set), nh_type, nh_upper_family, mask of rt_flags and 68 * NHF_DEFAULT 69 * 70 * Additionally, each nexthop gets assigned its unique index (nexthop index). 71 * It serves two purposes: first one is to ease the ability of userland programs to 72 * reference nexthops by their index. The second one allows lookup algorithms to 73 * to store index instead of pointer (2 bytes vs 8) as a lookup result. 74 * All nexthops are stored in the resizable hash table. 75 * 76 * Basically, this file revolves around supporting 3 functions: 77 * 1) nhop_create_from_info / nhop_create_from_nhop, which contains all 78 * business logic on filling the nexthop fields based on the provided request. 79 * 2) nhop_get(), which gets a usable referenced nexthops. 80 * 81 * Conventions: 82 * 1) non-exported functions start with verb 83 * 2) exported function starts with the subsystem prefix: "nhop" 84 */ 85 86 static int dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w); 87 88 static int finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link); 89 static struct ifnet *get_aifp(const struct nhop_object *nh); 90 static void fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, const struct ifnet *ifp); 91 92 static void destroy_nhop_epoch(epoch_context_t ctx); 93 static void destroy_nhop(struct nhop_object *nh); 94 95 _Static_assert(__offsetof(struct nhop_object, nh_ifp) == 32, 96 "nhop_object: wrong nh_ifp offset"); 97 _Static_assert(sizeof(struct nhop_object) <= 128, 98 "nhop_object: size exceeds 128 bytes"); 99 100 static uma_zone_t nhops_zone; /* Global zone for each and every nexthop */ 101 102 #define NHOP_OBJECT_ALIGNED_SIZE roundup2(sizeof(struct nhop_object), \ 103 2 * CACHE_LINE_SIZE) 104 #define NHOP_PRIV_ALIGNED_SIZE roundup2(sizeof(struct nhop_priv), \ 105 2 * CACHE_LINE_SIZE) 106 void 107 nhops_init(void) 108 { 109 110 nhops_zone = uma_zcreate("routing nhops", 111 NHOP_OBJECT_ALIGNED_SIZE + NHOP_PRIV_ALIGNED_SIZE, 112 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 113 } 114 115 /* 116 * Fetches the interface of source address used by the route. 117 * In all cases except interface-address-route it would be the 118 * same as the transmit interfaces. 119 * However, for the interface address this function will return 120 * this interface ifp instead of loopback. This is needed to support 121 * link-local IPv6 loopback communications. 122 * 123 * Returns found ifp. 124 */ 125 static struct ifnet * 126 get_aifp(const struct nhop_object *nh) 127 { 128 struct ifnet *aifp = NULL; 129 130 /* 131 * Adjust the "outgoing" interface. If we're going to loop 132 * the packet back to ourselves, the ifp would be the loopback 133 * interface. However, we'd rather know the interface associated 134 * to the destination address (which should probably be one of 135 * our own addresses). 136 */ 137 if ((nh->nh_ifp->if_flags & IFF_LOOPBACK) && 138 nh->gw_sa.sa_family == AF_LINK) { 139 aifp = ifnet_byindex(nh->gwl_sa.sdl_index); 140 if (aifp == NULL) { 141 FIB_NH_LOG(LOG_WARNING, nh, "unable to get aifp for %s index %d", 142 if_name(nh->nh_ifp), nh->gwl_sa.sdl_index); 143 } 144 } 145 146 if (aifp == NULL) 147 aifp = nh->nh_ifp; 148 149 return (aifp); 150 } 151 152 int 153 cmp_priv(const struct nhop_priv *_one, const struct nhop_priv *_two) 154 { 155 156 if (memcmp(_one->nh, _two->nh, NHOP_END_CMP) != 0) 157 return (0); 158 159 if (memcmp(_one, _two, NH_PRIV_END_CMP) != 0) 160 return (0); 161 162 return (1); 163 } 164 165 /* 166 * Conditionally sets @nh mtu data based on the @info data. 167 */ 168 static void 169 set_nhop_mtu_from_info(struct nhop_object *nh, const struct rt_addrinfo *info) 170 { 171 if (info->rti_mflags & RTV_MTU) 172 nhop_set_mtu(nh, info->rti_rmx->rmx_mtu, true); 173 } 174 175 /* 176 * Fills in shorted link-level sockadd version suitable to be stored inside the 177 * nexthop gateway buffer. 178 */ 179 static void 180 fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, const struct ifnet *ifp) 181 { 182 183 bzero(sdl, sizeof(struct sockaddr_dl_short)); 184 sdl->sdl_family = AF_LINK; 185 sdl->sdl_len = sizeof(struct sockaddr_dl_short); 186 sdl->sdl_index = ifp->if_index; 187 sdl->sdl_type = ifp->if_type; 188 } 189 190 static int 191 set_nhop_gw_from_info(struct nhop_object *nh, struct rt_addrinfo *info) 192 { 193 struct sockaddr *gw; 194 195 gw = info->rti_info[RTAX_GATEWAY]; 196 MPASS(gw != NULL); 197 bool is_gw = info->rti_flags & RTF_GATEWAY; 198 199 if ((gw->sa_family == AF_LINK) && !is_gw) { 200 201 /* 202 * Interface route with interface specified by the interface 203 * index in sockadd_dl structure. It is used in the IPv6 loopback 204 * output code, where we need to preserve the original interface 205 * to maintain proper scoping. 206 * Despite the fact that nexthop code stores original interface 207 * in the separate field (nh_aifp, see below), write AF_LINK 208 * compatible sa with shorter total length. 209 */ 210 struct sockaddr_dl *sdl = (struct sockaddr_dl *)gw; 211 struct ifnet *ifp = ifnet_byindex(sdl->sdl_index); 212 if (ifp == NULL) { 213 FIB_NH_LOG(LOG_DEBUG, nh, "error: invalid ifindex %d", 214 sdl->sdl_index); 215 return (EINVAL); 216 } 217 nhop_set_direct_gw(nh, ifp); 218 } else { 219 220 /* 221 * Multiple options here: 222 * 223 * 1) RTF_GATEWAY with IPv4/IPv6 gateway data 224 * 2) Interface route with IPv4/IPv6 address of the 225 * matching interface. Some routing daemons do that 226 * instead of specifying ifindex in AF_LINK. 227 * 228 * In both cases, save the original nexthop to make the callers 229 * happy. 230 */ 231 if (!nhop_set_gw(nh, gw, is_gw)) 232 return (EINVAL); 233 } 234 return (0); 235 } 236 237 static void 238 set_nhop_expire_from_info(struct nhop_object *nh, const struct rt_addrinfo *info) 239 { 240 uint32_t nh_expire = 0; 241 242 /* Kernel -> userland timebase conversion. */ 243 if ((info->rti_mflags & RTV_EXPIRE) && (info->rti_rmx->rmx_expire > 0)) 244 nh_expire = info->rti_rmx->rmx_expire - time_second + time_uptime; 245 nhop_set_expire(nh, nh_expire); 246 } 247 248 /* 249 * Creates a new nexthop based on the information in @info. 250 * 251 * Returns: 252 * 0 on success, filling @nh_ret with the desired nexthop object ptr 253 * errno otherwise 254 */ 255 int 256 nhop_create_from_info(struct rib_head *rnh, struct rt_addrinfo *info, 257 struct nhop_object **nh_ret) 258 { 259 int error; 260 261 NET_EPOCH_ASSERT(); 262 263 MPASS(info->rti_ifa != NULL); 264 MPASS(info->rti_ifp != NULL); 265 266 if (info->rti_info[RTAX_GATEWAY] == NULL) { 267 FIB_RH_LOG(LOG_DEBUG, rnh, "error: empty gateway"); 268 return (EINVAL); 269 } 270 271 struct nhop_object *nh = nhop_alloc(rnh->rib_fibnum, rnh->rib_family); 272 if (nh == NULL) 273 return (ENOMEM); 274 275 if ((error = set_nhop_gw_from_info(nh, info)) != 0) { 276 nhop_free(nh); 277 return (error); 278 } 279 nhop_set_transmit_ifp(nh, info->rti_ifp); 280 281 nhop_set_blackhole(nh, info->rti_flags & (RTF_BLACKHOLE | RTF_REJECT)); 282 283 error = rnh->rnh_set_nh_pfxflags(rnh->rib_fibnum, info->rti_info[RTAX_DST], 284 info->rti_info[RTAX_NETMASK], nh); 285 286 nhop_set_redirect(nh, info->rti_flags & RTF_DYNAMIC); 287 nhop_set_pinned(nh, info->rti_flags & RTF_PINNED); 288 set_nhop_expire_from_info(nh, info); 289 nhop_set_rtflags(nh, info->rti_flags); 290 291 set_nhop_mtu_from_info(nh, info); 292 nhop_set_src(nh, info->rti_ifa); 293 294 /* 295 * The remaining fields are either set from nh_preadd hook 296 * or are computed from the provided data 297 */ 298 *nh_ret = nhop_get_nhop(nh, &error); 299 300 return (error); 301 } 302 303 /* 304 * Gets linked nhop using the provided @nh nexhop data. 305 * If linked nhop is found, returns it, freeing the provided one. 306 * If there is no such nexthop, attaches the remaining data to the 307 * provided nexthop and links it. 308 * 309 * Returns 0 on success, storing referenced nexthop in @pnh. 310 * Otherwise, errno is returned. 311 */ 312 struct nhop_object * 313 nhop_get_nhop(struct nhop_object *nh, int *perror) 314 { 315 struct rib_head *rnh = nhop_get_rh(nh); 316 317 if (__predict_false(rnh == NULL)) { 318 *perror = EAFNOSUPPORT; 319 nhop_free(nh); 320 return (NULL); 321 } 322 323 return (nhop_get_nhop_internal(rnh, nh, perror)); 324 } 325 326 struct nhop_object * 327 nhop_get_nhop_internal(struct rib_head *rnh, struct nhop_object *nh, int *perror) 328 { 329 struct nhop_priv *tmp_priv; 330 int error; 331 332 nh->nh_aifp = get_aifp(nh); 333 334 /* Give the protocols chance to augment nexthop properties */ 335 error = rnh->rnh_augment_nh(rnh->rib_fibnum, nh); 336 if (error != 0) { 337 nhop_free(nh); 338 *perror = error; 339 return (NULL); 340 } 341 342 tmp_priv = find_nhop(rnh->nh_control, nh->nh_priv); 343 if (tmp_priv != NULL) { 344 nhop_free(nh); 345 *perror = 0; 346 return (tmp_priv->nh); 347 } 348 349 /* 350 * Existing nexthop not found, need to create new one. 351 * Note: multiple simultaneous requests 352 * can result in multiple equal nexhops existing in the 353 * nexthop table. This is not a not a problem until the 354 * relative number of such nexthops is significant, which 355 * is extremely unlikely. 356 */ 357 *perror = finalize_nhop(rnh->nh_control, nh, true); 358 return (*perror == 0 ? nh : NULL); 359 } 360 361 /* 362 * Gets referenced but unlinked nhop. 363 * Alocates/references the remaining bits of the nexthop data, so 364 * it can be safely linked later or used as a clone source. 365 * 366 * Returns 0 on success. 367 */ 368 int 369 nhop_get_unlinked(struct nhop_object *nh) 370 { 371 struct rib_head *rnh = nhop_get_rh(nh); 372 373 if (__predict_false(rnh == NULL)) { 374 nhop_free(nh); 375 return (EAFNOSUPPORT); 376 } 377 378 nh->nh_aifp = get_aifp(nh); 379 380 return (finalize_nhop(rnh->nh_control, nh, false)); 381 } 382 383 384 /* 385 * Update @nh with data supplied in @info. 386 * This is a helper function to support route changes. 387 * 388 * It limits the changes that can be done to the route to the following: 389 * 1) all combination of gateway changes 390 * 2) route flags (FLAG[123],STATIC) 391 * 3) route MTU 392 * 393 * Returns: 394 * 0 on success, errno otherwise 395 */ 396 static int 397 alter_nhop_from_info(struct nhop_object *nh, struct rt_addrinfo *info) 398 { 399 struct sockaddr *info_gw; 400 int error; 401 402 /* Update MTU if set in the request*/ 403 set_nhop_mtu_from_info(nh, info); 404 405 /* Only RTF_FLAG[123] and RTF_STATIC */ 406 uint32_t rt_flags = nhop_get_rtflags(nh) & ~RT_CHANGE_RTFLAGS_MASK; 407 rt_flags |= info->rti_flags & RT_CHANGE_RTFLAGS_MASK; 408 nhop_set_rtflags(nh, rt_flags); 409 410 /* Consider gateway change */ 411 info_gw = info->rti_info[RTAX_GATEWAY]; 412 if (info_gw != NULL) { 413 error = set_nhop_gw_from_info(nh, info); 414 if (error != 0) 415 return (error); 416 } 417 418 if (info->rti_ifa != NULL) 419 nhop_set_src(nh, info->rti_ifa); 420 if (info->rti_ifp != NULL) 421 nhop_set_transmit_ifp(nh, info->rti_ifp); 422 423 return (0); 424 } 425 426 /* 427 * Creates new nexthop based on @nh_orig and augmentation data from @info. 428 * Helper function used in the route changes, please see 429 * alter_nhop_from_info() comments for more details. 430 * 431 * Returns: 432 * 0 on success, filling @nh_ret with the desired nexthop object 433 * errno otherwise 434 */ 435 int 436 nhop_create_from_nhop(struct rib_head *rnh, const struct nhop_object *nh_orig, 437 struct rt_addrinfo *info, struct nhop_object **pnh) 438 { 439 struct nhop_object *nh; 440 int error; 441 442 NET_EPOCH_ASSERT(); 443 444 nh = nhop_alloc(rnh->rib_fibnum, rnh->rib_family); 445 if (nh == NULL) 446 return (ENOMEM); 447 448 nhop_copy(nh, nh_orig); 449 450 error = alter_nhop_from_info(nh, info); 451 if (error != 0) { 452 nhop_free(nh); 453 return (error); 454 } 455 456 *pnh = nhop_get_nhop(nh, &error); 457 458 return (error); 459 } 460 461 static bool 462 reference_nhop_deps(struct nhop_object *nh) 463 { 464 if (!ifa_try_ref(nh->nh_ifa)) 465 return (false); 466 nh->nh_aifp = get_aifp(nh); 467 if (!if_try_ref(nh->nh_aifp)) { 468 ifa_free(nh->nh_ifa); 469 return (false); 470 } 471 FIB_NH_LOG(LOG_DEBUG2, nh, "nh_aifp: %s nh_ifp %s", 472 if_name(nh->nh_aifp), if_name(nh->nh_ifp)); 473 if (!if_try_ref(nh->nh_ifp)) { 474 ifa_free(nh->nh_ifa); 475 if_rele(nh->nh_aifp); 476 return (false); 477 } 478 479 return (true); 480 } 481 482 /* 483 * Alocates/references the remaining bits of nexthop data and links 484 * it to the hash table. 485 * Returns 0 if successful, 486 * errno otherwise. @nh_priv is freed in case of error. 487 */ 488 static int 489 finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link) 490 { 491 492 /* Allocate per-cpu packet counter */ 493 nh->nh_pksent = counter_u64_alloc(M_NOWAIT); 494 if (nh->nh_pksent == NULL) { 495 nhop_free(nh); 496 RTSTAT_INC(rts_nh_alloc_failure); 497 FIB_NH_LOG(LOG_WARNING, nh, "counter_u64_alloc() failed"); 498 return (ENOMEM); 499 } 500 501 if (!reference_nhop_deps(nh)) { 502 counter_u64_free(nh->nh_pksent); 503 nhop_free(nh); 504 RTSTAT_INC(rts_nh_alloc_failure); 505 FIB_NH_LOG(LOG_WARNING, nh, "interface reference failed"); 506 return (EAGAIN); 507 } 508 509 /* Save vnet to ease destruction */ 510 nh->nh_priv->nh_vnet = curvnet; 511 512 /* Please see nhop_free() comments on the initial value */ 513 refcount_init(&nh->nh_priv->nh_linked, 2); 514 515 MPASS(nh->nh_priv->nh_fibnum == ctl->ctl_rh->rib_fibnum); 516 517 if (!link) { 518 refcount_release(&nh->nh_priv->nh_linked); 519 NHOPS_WLOCK(ctl); 520 nh->nh_priv->nh_finalized = 1; 521 NHOPS_WUNLOCK(ctl); 522 } else if (link_nhop(ctl, nh->nh_priv) == 0) { 523 /* 524 * Adding nexthop to the datastructures 525 * failed. Call destructor w/o waiting for 526 * the epoch end, as nexthop is not used 527 * and return. 528 */ 529 char nhbuf[NHOP_PRINT_BUFSIZE]; 530 FIB_NH_LOG(LOG_WARNING, nh, "failed to link %s", 531 nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); 532 destroy_nhop(nh); 533 534 return (ENOBUFS); 535 } 536 537 IF_DEBUG_LEVEL(LOG_DEBUG) { 538 char nhbuf[NHOP_PRINT_BUFSIZE] __unused; 539 FIB_NH_LOG(LOG_DEBUG, nh, "finalized: %s", 540 nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); 541 } 542 543 return (0); 544 } 545 546 static void 547 destroy_nhop(struct nhop_object *nh) 548 { 549 if_rele(nh->nh_ifp); 550 if_rele(nh->nh_aifp); 551 ifa_free(nh->nh_ifa); 552 counter_u64_free(nh->nh_pksent); 553 554 uma_zfree(nhops_zone, nh); 555 } 556 557 /* 558 * Epoch callback indicating nhop is safe to destroy 559 */ 560 static void 561 destroy_nhop_epoch(epoch_context_t ctx) 562 { 563 struct nhop_priv *nh_priv; 564 565 nh_priv = __containerof(ctx, struct nhop_priv, nh_epoch_ctx); 566 567 destroy_nhop(nh_priv->nh); 568 } 569 570 void 571 nhop_ref_object(struct nhop_object *nh) 572 { 573 u_int old __diagused; 574 575 old = refcount_acquire(&nh->nh_priv->nh_refcnt); 576 KASSERT(old > 0, ("%s: nhop object %p has 0 refs", __func__, nh)); 577 } 578 579 int 580 nhop_try_ref_object(struct nhop_object *nh) 581 { 582 583 return (refcount_acquire_if_not_zero(&nh->nh_priv->nh_refcnt)); 584 } 585 586 void 587 nhop_free(struct nhop_object *nh) 588 { 589 struct nh_control *ctl; 590 struct nhop_priv *nh_priv = nh->nh_priv; 591 struct epoch_tracker et; 592 593 if (!refcount_release(&nh_priv->nh_refcnt)) 594 return; 595 596 /* allows to use nhop_free() during nhop init */ 597 if (__predict_false(nh_priv->nh_finalized == 0)) { 598 uma_zfree(nhops_zone, nh); 599 return; 600 } 601 602 IF_DEBUG_LEVEL(LOG_DEBUG) { 603 char nhbuf[NHOP_PRINT_BUFSIZE] __unused; 604 FIB_NH_LOG(LOG_DEBUG, nh, "deleting %s", 605 nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); 606 } 607 608 /* 609 * There are only 2 places, where nh_linked can be decreased: 610 * rib destroy (nhops_destroy_rib) and this function. 611 * nh_link can never be increased. 612 * 613 * Hence, use initial value of 2 to make use of 614 * refcount_release_if_not_last(). 615 * 616 * There can be two scenarious when calling this function: 617 * 618 * 1) nh_linked value is 2. This means that either 619 * nhops_destroy_rib() has not been called OR it is running, 620 * but we are guaranteed that nh_control won't be freed in 621 * this epoch. Hence, nexthop can be safely unlinked. 622 * 623 * 2) nh_linked value is 1. In that case, nhops_destroy_rib() 624 * has been called and nhop unlink can be skipped. 625 */ 626 627 NET_EPOCH_ENTER(et); 628 if (refcount_release_if_not_last(&nh_priv->nh_linked)) { 629 ctl = nh_priv->nh_control; 630 if (unlink_nhop(ctl, nh_priv) == NULL) { 631 /* Do not try to reclaim */ 632 char nhbuf[NHOP_PRINT_BUFSIZE]; 633 FIB_NH_LOG(LOG_WARNING, nh, "failed to unlink %s", 634 nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); 635 NET_EPOCH_EXIT(et); 636 return; 637 } 638 } 639 NET_EPOCH_EXIT(et); 640 641 epoch_call(net_epoch_preempt, destroy_nhop_epoch, 642 &nh_priv->nh_epoch_ctx); 643 } 644 645 void 646 nhop_ref_any(struct nhop_object *nh) 647 { 648 #ifdef ROUTE_MPATH 649 if (!NH_IS_NHGRP(nh)) 650 nhop_ref_object(nh); 651 else 652 nhgrp_ref_object((struct nhgrp_object *)nh); 653 #else 654 nhop_ref_object(nh); 655 #endif 656 } 657 658 void 659 nhop_free_any(struct nhop_object *nh) 660 { 661 662 #ifdef ROUTE_MPATH 663 if (!NH_IS_NHGRP(nh)) 664 nhop_free(nh); 665 else 666 nhgrp_free((struct nhgrp_object *)nh); 667 #else 668 nhop_free(nh); 669 #endif 670 } 671 672 /* Nhop-related methods */ 673 674 /* 675 * Allocates an empty unlinked nhop object. 676 * Returns object pointer or NULL on failure 677 */ 678 struct nhop_object * 679 nhop_alloc(uint32_t fibnum, int family) 680 { 681 struct nhop_object *nh; 682 struct nhop_priv *nh_priv; 683 684 nh = (struct nhop_object *)uma_zalloc(nhops_zone, M_NOWAIT | M_ZERO); 685 if (__predict_false(nh == NULL)) 686 return (NULL); 687 688 nh_priv = (struct nhop_priv *)((char *)nh + NHOP_OBJECT_ALIGNED_SIZE); 689 nh->nh_priv = nh_priv; 690 nh_priv->nh = nh; 691 692 nh_priv->nh_upper_family = family; 693 nh_priv->nh_fibnum = fibnum; 694 695 /* Setup refcount early to allow nhop_free() to work */ 696 refcount_init(&nh_priv->nh_refcnt, 1); 697 698 return (nh); 699 } 700 701 void 702 nhop_copy(struct nhop_object *nh, const struct nhop_object *nh_orig) 703 { 704 struct nhop_priv *nh_priv = nh->nh_priv; 705 706 nh->nh_flags = nh_orig->nh_flags; 707 nh->nh_mtu = nh_orig->nh_mtu; 708 memcpy(&nh->gw_sa, &nh_orig->gw_sa, nh_orig->gw_sa.sa_len); 709 nh->nh_ifp = nh_orig->nh_ifp; 710 nh->nh_ifa = nh_orig->nh_ifa; 711 nh->nh_aifp = nh_orig->nh_aifp; 712 713 nh_priv->nh_upper_family = nh_orig->nh_priv->nh_upper_family; 714 nh_priv->nh_neigh_family = nh_orig->nh_priv->nh_neigh_family; 715 nh_priv->nh_type = nh_orig->nh_priv->nh_type; 716 nh_priv->rt_flags = nh_orig->nh_priv->rt_flags; 717 nh_priv->nh_fibnum = nh_orig->nh_priv->nh_fibnum; 718 } 719 720 void 721 nhop_set_direct_gw(struct nhop_object *nh, struct ifnet *ifp) 722 { 723 nh->nh_flags &= ~NHF_GATEWAY; 724 nh->nh_priv->rt_flags &= ~RTF_GATEWAY; 725 nh->nh_priv->nh_neigh_family = nh->nh_priv->nh_upper_family; 726 727 fill_sdl_from_ifp(&nh->gwl_sa, ifp); 728 memset(&nh->gw_buf[nh->gw_sa.sa_len], 0, sizeof(nh->gw_buf) - nh->gw_sa.sa_len); 729 } 730 731 bool 732 nhop_check_gateway(int upper_family, int neigh_family) 733 { 734 if (upper_family == neigh_family) 735 return (true); 736 else if (neigh_family == AF_UNSPEC || neigh_family == AF_LINK) 737 return (true); 738 #if defined(INET) && defined(INET6) 739 else if (upper_family == AF_INET && neigh_family == AF_INET6 && 740 rib_can_4o6_nhop()) 741 return (true); 742 #endif 743 else 744 return (false); 745 } 746 747 /* 748 * Sets gateway for the nexthop. 749 * It can be "normal" gateway with is_gw set or a special form of 750 * adding interface route, refering to it by specifying local interface 751 * address. In that case is_gw is set to false. 752 */ 753 bool 754 nhop_set_gw(struct nhop_object *nh, const struct sockaddr *gw, bool is_gw) 755 { 756 if (gw->sa_len > sizeof(nh->gw_buf)) { 757 FIB_NH_LOG(LOG_DEBUG, nh, "nhop SA size too big: AF %d len %u", 758 gw->sa_family, gw->sa_len); 759 return (false); 760 } 761 762 if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, gw->sa_family)) { 763 FIB_NH_LOG(LOG_DEBUG, nh, 764 "error: invalid dst/gateway family combination (%d, %d)", 765 nh->nh_priv->nh_upper_family, gw->sa_family); 766 return (false); 767 } 768 769 memcpy(&nh->gw_sa, gw, gw->sa_len); 770 memset(&nh->gw_buf[gw->sa_len], 0, sizeof(nh->gw_buf) - gw->sa_len); 771 772 if (is_gw) { 773 nh->nh_flags |= NHF_GATEWAY; 774 nh->nh_priv->rt_flags |= RTF_GATEWAY; 775 nh->nh_priv->nh_neigh_family = gw->sa_family; 776 } else { 777 nh->nh_flags &= ~NHF_GATEWAY; 778 nh->nh_priv->rt_flags &= ~RTF_GATEWAY; 779 nh->nh_priv->nh_neigh_family = nh->nh_priv->nh_upper_family; 780 } 781 782 return (true); 783 } 784 785 bool 786 nhop_set_upper_family(struct nhop_object *nh, int family) 787 { 788 if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, family)) { 789 FIB_NH_LOG(LOG_DEBUG, nh, 790 "error: invalid upper/neigh family combination (%d, %d)", 791 nh->nh_priv->nh_upper_family, family); 792 return (false); 793 } 794 795 nh->nh_priv->nh_upper_family = family; 796 return (true); 797 } 798 799 void 800 nhop_set_broadcast(struct nhop_object *nh, bool is_broadcast) 801 { 802 if (is_broadcast) { 803 nh->nh_flags |= NHF_BROADCAST; 804 nh->nh_priv->rt_flags |= RTF_BROADCAST; 805 } else { 806 nh->nh_flags &= ~NHF_BROADCAST; 807 nh->nh_priv->rt_flags &= ~RTF_BROADCAST; 808 } 809 } 810 811 void 812 nhop_set_blackhole(struct nhop_object *nh, int blackhole_rt_flag) 813 { 814 nh->nh_flags &= ~(NHF_BLACKHOLE | NHF_REJECT); 815 nh->nh_priv->rt_flags &= ~(RTF_BLACKHOLE | RTF_REJECT); 816 switch (blackhole_rt_flag) { 817 case RTF_BLACKHOLE: 818 nh->nh_flags |= NHF_BLACKHOLE; 819 nh->nh_priv->rt_flags |= RTF_BLACKHOLE; 820 break; 821 case RTF_REJECT: 822 nh->nh_flags |= NHF_REJECT; 823 nh->nh_priv->rt_flags |= RTF_REJECT; 824 break; 825 } 826 } 827 828 void 829 nhop_set_redirect(struct nhop_object *nh, bool is_redirect) 830 { 831 if (is_redirect) { 832 nh->nh_priv->rt_flags |= RTF_DYNAMIC; 833 nh->nh_flags |= NHF_REDIRECT; 834 } else { 835 nh->nh_priv->rt_flags &= ~RTF_DYNAMIC; 836 nh->nh_flags &= ~NHF_REDIRECT; 837 } 838 } 839 840 void 841 nhop_set_pinned(struct nhop_object *nh, bool is_pinned) 842 { 843 if (is_pinned) 844 nh->nh_priv->rt_flags |= RTF_PINNED; 845 else 846 nh->nh_priv->rt_flags &= ~RTF_PINNED; 847 } 848 849 uint32_t 850 nhop_get_idx(const struct nhop_object *nh) 851 { 852 853 return (nh->nh_priv->nh_idx); 854 } 855 856 uint32_t 857 nhop_get_uidx(const struct nhop_object *nh) 858 { 859 return (nh->nh_priv->nh_uidx); 860 } 861 862 void 863 nhop_set_uidx(struct nhop_object *nh, uint32_t uidx) 864 { 865 nh->nh_priv->nh_uidx = uidx; 866 } 867 868 enum nhop_type 869 nhop_get_type(const struct nhop_object *nh) 870 { 871 872 return (nh->nh_priv->nh_type); 873 } 874 875 void 876 nhop_set_type(struct nhop_object *nh, enum nhop_type nh_type) 877 { 878 879 nh->nh_priv->nh_type = nh_type; 880 } 881 882 int 883 nhop_get_rtflags(const struct nhop_object *nh) 884 { 885 886 return (nh->nh_priv->rt_flags); 887 } 888 889 /* 890 * Sets generic rtflags that are not covered by other functions. 891 */ 892 void 893 nhop_set_rtflags(struct nhop_object *nh, int rt_flags) 894 { 895 nh->nh_priv->rt_flags &= ~RT_SET_RTFLAGS_MASK; 896 nh->nh_priv->rt_flags |= (rt_flags & RT_SET_RTFLAGS_MASK); 897 } 898 899 /* 900 * Sets flags that are specific to the prefix (NHF_HOST or NHF_DEFAULT). 901 */ 902 void 903 nhop_set_pxtype_flag(struct nhop_object *nh, int nh_flag) 904 { 905 if (nh_flag == NHF_HOST) { 906 nh->nh_flags |= NHF_HOST; 907 nh->nh_flags &= ~NHF_DEFAULT; 908 nh->nh_priv->rt_flags |= RTF_HOST; 909 } else if (nh_flag == NHF_DEFAULT) { 910 nh->nh_flags |= NHF_DEFAULT; 911 nh->nh_flags &= ~NHF_HOST; 912 nh->nh_priv->rt_flags &= ~RTF_HOST; 913 } else { 914 nh->nh_flags &= ~(NHF_HOST | NHF_DEFAULT); 915 nh->nh_priv->rt_flags &= ~RTF_HOST; 916 } 917 } 918 919 /* 920 * Sets nhop MTU. Sets RTF_FIXEDMTU if mtu is explicitly 921 * specified by userland. 922 */ 923 void 924 nhop_set_mtu(struct nhop_object *nh, uint32_t mtu, bool from_user) 925 { 926 if (from_user) { 927 if (mtu != 0) 928 nh->nh_priv->rt_flags |= RTF_FIXEDMTU; 929 else 930 nh->nh_priv->rt_flags &= ~RTF_FIXEDMTU; 931 } 932 nh->nh_mtu = mtu; 933 } 934 935 void 936 nhop_set_src(struct nhop_object *nh, struct ifaddr *ifa) 937 { 938 nh->nh_ifa = ifa; 939 } 940 941 void 942 nhop_set_transmit_ifp(struct nhop_object *nh, struct ifnet *ifp) 943 { 944 nh->nh_ifp = ifp; 945 } 946 947 948 struct vnet * 949 nhop_get_vnet(const struct nhop_object *nh) 950 { 951 952 return (nh->nh_priv->nh_vnet); 953 } 954 955 struct nhop_object * 956 nhop_select_func(struct nhop_object *nh, uint32_t flowid) 957 { 958 959 return (nhop_select(nh, flowid)); 960 } 961 962 /* 963 * Returns address family of the traffic uses the nexthop. 964 */ 965 int 966 nhop_get_upper_family(const struct nhop_object *nh) 967 { 968 return (nh->nh_priv->nh_upper_family); 969 } 970 971 /* 972 * Returns address family of the LLE or gateway that is used 973 * to forward the traffic to. 974 */ 975 int 976 nhop_get_neigh_family(const struct nhop_object *nh) 977 { 978 return (nh->nh_priv->nh_neigh_family); 979 } 980 981 uint32_t 982 nhop_get_fibnum(const struct nhop_object *nh) 983 { 984 return (nh->nh_priv->nh_fibnum); 985 } 986 987 void 988 nhop_set_fibnum(struct nhop_object *nh, uint32_t fibnum) 989 { 990 nh->nh_priv->nh_fibnum = fibnum; 991 } 992 993 uint32_t 994 nhop_get_expire(const struct nhop_object *nh) 995 { 996 return (nh->nh_priv->nh_expire); 997 } 998 999 void 1000 nhop_set_expire(struct nhop_object *nh, uint32_t expire) 1001 { 1002 MPASS(!NH_IS_LINKED(nh)); 1003 nh->nh_priv->nh_expire = expire; 1004 } 1005 1006 struct rib_head * 1007 nhop_get_rh(const struct nhop_object *nh) 1008 { 1009 uint32_t fibnum = nhop_get_fibnum(nh); 1010 int family = nhop_get_neigh_family(nh); 1011 1012 return (rt_tables_get_rnh(fibnum, family)); 1013 } 1014 1015 void 1016 nhops_update_ifmtu(struct rib_head *rh, struct ifnet *ifp, uint32_t mtu) 1017 { 1018 struct nh_control *ctl; 1019 struct nhop_priv *nh_priv; 1020 struct nhop_object *nh; 1021 1022 ctl = rh->nh_control; 1023 1024 NHOPS_WLOCK(ctl); 1025 CHT_SLIST_FOREACH(&ctl->nh_head, nhops, nh_priv) { 1026 nh = nh_priv->nh; 1027 if (nh->nh_ifp == ifp) { 1028 if ((nh_priv->rt_flags & RTF_FIXEDMTU) == 0 || 1029 nh->nh_mtu > mtu) { 1030 /* Update MTU directly */ 1031 nh->nh_mtu = mtu; 1032 } 1033 } 1034 } CHT_SLIST_FOREACH_END; 1035 NHOPS_WUNLOCK(ctl); 1036 1037 } 1038 1039 /* 1040 * Prints nexthop @nh data in the provided @buf. 1041 * Example: nh#33/inet/em0/192.168.0.1 1042 */ 1043 char * 1044 nhop_print_buf(const struct nhop_object *nh, char *buf, size_t bufsize) 1045 { 1046 #if defined(INET) || defined(INET6) 1047 char abuf[INET6_ADDRSTRLEN]; 1048 #endif 1049 struct nhop_priv *nh_priv = nh->nh_priv; 1050 const char *upper_str = rib_print_family(nh->nh_priv->nh_upper_family); 1051 1052 switch (nh->gw_sa.sa_family) { 1053 #ifdef INET 1054 case AF_INET: 1055 inet_ntop(AF_INET, &nh->gw4_sa.sin_addr, abuf, sizeof(abuf)); 1056 snprintf(buf, bufsize, "nh#%d/%s/%s/%s", nh_priv->nh_idx, upper_str, 1057 if_name(nh->nh_ifp), abuf); 1058 break; 1059 #endif 1060 #ifdef INET6 1061 case AF_INET6: 1062 inet_ntop(AF_INET6, &nh->gw6_sa.sin6_addr, abuf, sizeof(abuf)); 1063 snprintf(buf, bufsize, "nh#%d/%s/%s/%s", nh_priv->nh_idx, upper_str, 1064 if_name(nh->nh_ifp), abuf); 1065 break; 1066 #endif 1067 case AF_LINK: 1068 snprintf(buf, bufsize, "nh#%d/%s/%s/resolve", nh_priv->nh_idx, upper_str, 1069 if_name(nh->nh_ifp)); 1070 break; 1071 default: 1072 snprintf(buf, bufsize, "nh#%d/%s/%s/????", nh_priv->nh_idx, upper_str, 1073 if_name(nh->nh_ifp)); 1074 break; 1075 } 1076 1077 return (buf); 1078 } 1079 1080 char * 1081 nhop_print_buf_any(const struct nhop_object *nh, char *buf, size_t bufsize) 1082 { 1083 #ifdef ROUTE_MPATH 1084 if (NH_IS_NHGRP(nh)) 1085 return (nhgrp_print_buf((const struct nhgrp_object *)nh, buf, bufsize)); 1086 else 1087 #endif 1088 return (nhop_print_buf(nh, buf, bufsize)); 1089 } 1090 1091 /* 1092 * Dumps a single entry to sysctl buffer. 1093 * 1094 * Layout: 1095 * rt_msghdr - generic RTM header to allow users to skip non-understood messages 1096 * nhop_external - nexhop description structure (with length) 1097 * nhop_addrs - structure encapsulating GW/SRC sockaddrs 1098 */ 1099 static int 1100 dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w) 1101 { 1102 struct { 1103 struct rt_msghdr rtm; 1104 struct nhop_external nhe; 1105 struct nhop_addrs na; 1106 } arpc; 1107 struct nhop_external *pnhe; 1108 struct sockaddr *gw_sa, *src_sa; 1109 struct sockaddr_storage ss; 1110 size_t addrs_len; 1111 int error; 1112 1113 memset(&arpc, 0, sizeof(arpc)); 1114 1115 arpc.rtm.rtm_msglen = sizeof(arpc); 1116 arpc.rtm.rtm_version = RTM_VERSION; 1117 arpc.rtm.rtm_type = RTM_GET; 1118 //arpc.rtm.rtm_flags = RTF_UP; 1119 arpc.rtm.rtm_flags = nh->nh_priv->rt_flags; 1120 1121 /* nhop_external */ 1122 pnhe = &arpc.nhe; 1123 pnhe->nh_len = sizeof(struct nhop_external); 1124 pnhe->nh_idx = nh->nh_priv->nh_idx; 1125 pnhe->nh_fib = rh->rib_fibnum; 1126 pnhe->ifindex = nh->nh_ifp->if_index; 1127 pnhe->aifindex = nh->nh_aifp->if_index; 1128 pnhe->nh_family = nh->nh_priv->nh_upper_family; 1129 pnhe->nh_type = nh->nh_priv->nh_type; 1130 pnhe->nh_mtu = nh->nh_mtu; 1131 pnhe->nh_flags = nh->nh_flags; 1132 1133 memcpy(pnhe->nh_prepend, nh->nh_prepend, sizeof(nh->nh_prepend)); 1134 pnhe->prepend_len = nh->nh_prepend_len; 1135 pnhe->nh_refcount = nh->nh_priv->nh_refcnt; 1136 pnhe->nh_pksent = counter_u64_fetch(nh->nh_pksent); 1137 1138 /* sockaddr container */ 1139 addrs_len = sizeof(struct nhop_addrs); 1140 arpc.na.gw_sa_off = addrs_len; 1141 gw_sa = (struct sockaddr *)&nh->gw4_sa; 1142 addrs_len += gw_sa->sa_len; 1143 1144 src_sa = nh->nh_ifa->ifa_addr; 1145 if (src_sa->sa_family == AF_LINK) { 1146 /* Shorten structure */ 1147 memset(&ss, 0, sizeof(struct sockaddr_storage)); 1148 fill_sdl_from_ifp((struct sockaddr_dl_short *)&ss, 1149 nh->nh_ifa->ifa_ifp); 1150 src_sa = (struct sockaddr *)&ss; 1151 } 1152 arpc.na.src_sa_off = addrs_len; 1153 addrs_len += src_sa->sa_len; 1154 1155 /* Write total container length */ 1156 arpc.na.na_len = addrs_len; 1157 1158 arpc.rtm.rtm_msglen += arpc.na.na_len - sizeof(struct nhop_addrs); 1159 1160 error = SYSCTL_OUT(w, &arpc, sizeof(arpc)); 1161 if (error == 0) 1162 error = SYSCTL_OUT(w, gw_sa, gw_sa->sa_len); 1163 if (error == 0) 1164 error = SYSCTL_OUT(w, src_sa, src_sa->sa_len); 1165 1166 return (error); 1167 } 1168 1169 uint32_t 1170 nhops_get_count(struct rib_head *rh) 1171 { 1172 struct nh_control *ctl; 1173 uint32_t count; 1174 1175 ctl = rh->nh_control; 1176 1177 NHOPS_RLOCK(ctl); 1178 count = ctl->nh_head.items_count; 1179 NHOPS_RUNLOCK(ctl); 1180 1181 return (count); 1182 } 1183 1184 int 1185 nhops_dump_sysctl(struct rib_head *rh, struct sysctl_req *w) 1186 { 1187 struct nh_control *ctl; 1188 struct nhop_priv *nh_priv; 1189 int error; 1190 1191 ctl = rh->nh_control; 1192 1193 NHOPS_RLOCK(ctl); 1194 FIB_RH_LOG(LOG_DEBUG, rh, "dump %u items", ctl->nh_head.items_count); 1195 CHT_SLIST_FOREACH(&ctl->nh_head, nhops, nh_priv) { 1196 error = dump_nhop_entry(rh, nh_priv->nh, w); 1197 if (error != 0) { 1198 NHOPS_RUNLOCK(ctl); 1199 return (error); 1200 } 1201 } CHT_SLIST_FOREACH_END; 1202 NHOPS_RUNLOCK(ctl); 1203 1204 return (0); 1205 } 1206