1/* 2 * Copyright (c) 2011-2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * Link-layer Reachability Record 31 * 32 * Each interface maintains a red-black tree which contains records related 33 * to the on-link nodes which we are interested in communicating with. Each 34 * record gets allocated and inserted into the tree in the following manner: 35 * upon processing an ARP announcement or reply from a known node (i.e. there 36 * exists a ARP route entry for the node), and if a link-layer reachability 37 * record for the node doesn't yet exist; and, upon processing a ND6 RS/RA/ 38 * NS/NA/redirect from a node, and if a link-layer reachability record for the 39 * node doesn't yet exist. 40 * 41 * Each newly created record is then referred to by the resolver route entry; 42 * if a record already exists, its reference count gets increased for the new 43 * resolver entry which now refers to it. A record gets removed from the tree 44 * and freed once its reference counts drops to zero, i.e. when there is no 45 * more resolver entry referring to it. 46 * 47 * A record contains the link-layer protocol (e.g. Ethertype IP/IPv6), the 48 * HW address of the sender, the "last heard from" timestamp (lr_lastrcvd) and 49 * the number of references made to it (lr_reqcnt). Because the key for each 50 * record in the red-black tree consists of the link-layer protocol, therefore 51 * the namespace for the records is partitioned based on the type of link-layer 52 * protocol, i.e. an Ethertype IP link-layer record is only referred to by one 53 * or more ARP entries; an Ethernet IPv6 link-layer record is only referred to 54 * by one or more ND6 entries. Therefore, lr_reqcnt represents the number of 55 * resolver entry references to the record for the same protocol family. 56 * 57 * Upon receiving packets from the network, the protocol's input callback 58 * (e.g. ether_inet{6}_input) informs the corresponding resolver (ARP/ND6) 59 * about the (link-layer) origin of the packet. This results in searching 60 * for a matching record in the red-black tree for the interface where the 61 * packet arrived on. If there's no match, no further processing takes place. 62 * Otherwise, the lr_lastrcvd timestamp of the record is updated. 63 * 64 * When an IP/IPv6 packet is transmitted to the resolver (i.e. the destination 65 * is on-link), ARP/ND6 records the "last spoken to" timestamp in the route 66 * entry ({la,ln}_lastused). 67 * 68 * The reachability of the on-link node is determined by the following logic, 69 * upon sending a packet thru the resolver: 70 * 71 * a) If the record is only used by exactly one resolver entry (lr_reqcnt 72 * is 1), i.e. the target host does not have IP/IPv6 aliases that we know 73 * of, check if lr_lastrcvd is "recent." If so, simply send the packet; 74 * otherwise, re-resolve the target node. 75 * 76 * b) If the record is shared by multiple resolver entries (lr_reqcnt is 77 * greater than 1), i.e. the target host has more than one IP/IPv6 aliases 78 * on the same network interface, we can't rely on lr_lastrcvd alone, as 79 * one of the IP/IPv6 aliases could have been silently moved to another 80 * node for which we don't have a link-layer record. If lr_lastrcvd is 81 * not "recent", we re-resolve the target node. Otherwise, we perform 82 * an additional check against {la,ln}_lastused to see whether it is also 83 * "recent", relative to lr_lastrcvd. If so, simply send the packet; 84 * otherwise, re-resolve the target node. 85 * 86 * The value for "recent" is configurable by adjusting the basetime value for 87 * net.link.ether.inet.arp_llreach_base or net.inet6.icmp6.nd6_llreach_base. 88 * The default basetime value is 30 seconds, and the actual expiration time 89 * is calculated by multiplying the basetime value with some random factor, 90 * which results in a number between 15 to 45 seconds. Setting the basetime 91 * value to 0 effectively disables this feature for the corresponding resolver. 92 * 93 * Assumptions: 94 * 95 * The above logic is based upon the following assumptions: 96 * 97 * i) Network traffics are mostly bi-directional, i.e. the act of sending 98 * packets to an on-link node would most likely cause us to receive 99 * packets from that node. 100 * 101 * ii) If the on-link node's IP/IPv6 address silently moves to another 102 * on-link node for which we are not aware of, non-unicast packets 103 * from the old node would trigger the record's lr_lastrcvd to be 104 * kept recent. 105 * 106 * We can mitigate the above by having the resolver check its {la,ln}_lastused 107 * timestamp at all times, i.e. not only when lr_reqcnt is greater than 1; but 108 * we currently optimize for the common cases. 109 */ 110 111#include <sys/param.h> 112#include <sys/systm.h> 113#include <sys/kernel.h> 114#include <sys/malloc.h> 115#include <sys/tree.h> 116#include <sys/sysctl.h> 117#include <sys/mcache.h> 118#include <sys/protosw.h> 119 120#include <dev/random/randomdev.h> 121 122#include <net/if_dl.h> 123#include <net/if.h> 124#include <net/if_var.h> 125#include <net/if_llreach.h> 126#include <net/dlil.h> 127#include <net/kpi_interface.h> 128#include <net/route.h> 129 130#include <kern/assert.h> 131#include <kern/locks.h> 132#include <kern/zalloc.h> 133 134#if INET6 135#include <netinet6/in6_var.h> 136#include <netinet6/nd6.h> 137#endif /* INET6 */ 138 139static unsigned int iflr_size; /* size of if_llreach */ 140static struct zone *iflr_zone; /* zone for if_llreach */ 141 142#define IFLR_ZONE_MAX 128 /* maximum elements in zone */ 143#define IFLR_ZONE_NAME "if_llreach" /* zone name */ 144 145static struct if_llreach *iflr_alloc(int); 146static void iflr_free(struct if_llreach *); 147static __inline int iflr_cmp(const struct if_llreach *, 148 const struct if_llreach *); 149static __inline int iflr_reachable(struct if_llreach *, int, u_int64_t); 150static int sysctl_llreach_ifinfo SYSCTL_HANDLER_ARGS; 151 152/* The following is protected by if_llreach_lock */ 153RB_GENERATE_PREV(ll_reach_tree, if_llreach, lr_link, iflr_cmp); 154 155SYSCTL_DECL(_net_link_generic_system); 156 157SYSCTL_NODE(_net_link_generic_system, OID_AUTO, llreach_info, 158 CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_llreach_ifinfo, 159 "Per-interface tree of source link-layer reachability records"); 160 161/* 162 * Link-layer reachability is based off node constants in RFC4861. 163 */ 164#if INET6 165#define LL_COMPUTE_RTIME(x) ND_COMPUTE_RTIME(x) 166#else 167#define LL_MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ 168#define LL_MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ 169#define LL_COMPUTE_RTIME(x) \ 170 (((LL_MIN_RANDOM_FACTOR * (x >> 10)) + (RandomULong() & \ 171 ((LL_MAX_RANDOM_FACTOR - LL_MIN_RANDOM_FACTOR) * (x >> 10)))) / 1000) 172#endif /* !INET6 */ 173 174void 175ifnet_llreach_init(void) 176{ 177 iflr_size = sizeof (struct if_llreach); 178 iflr_zone = zinit(iflr_size, 179 IFLR_ZONE_MAX * iflr_size, 0, IFLR_ZONE_NAME); 180 if (iflr_zone == NULL) { 181 panic("%s: failed allocating %s", __func__, IFLR_ZONE_NAME); 182 /* NOTREACHED */ 183 } 184 zone_change(iflr_zone, Z_EXPAND, TRUE); 185 zone_change(iflr_zone, Z_CALLERACCT, FALSE); 186} 187 188void 189ifnet_llreach_ifattach(struct ifnet *ifp, boolean_t reuse) 190{ 191 lck_rw_lock_exclusive(&ifp->if_llreach_lock); 192 /* Initialize link-layer source tree (if not already) */ 193 if (!reuse) 194 RB_INIT(&ifp->if_ll_srcs); 195 lck_rw_done(&ifp->if_llreach_lock); 196} 197 198void 199ifnet_llreach_ifdetach(struct ifnet *ifp) 200{ 201#pragma unused(ifp) 202 /* 203 * Nothing to do for now; the link-layer source tree might 204 * contain entries at this point, that are still referred 205 * to by route entries pointing to this ifp. 206 */ 207} 208 209/* 210 * Link-layer source tree comparison function. 211 * 212 * An ordered predicate is necessary; bcmp() is not documented to return 213 * an indication of order, memcmp() is, and is an ISO C99 requirement. 214 */ 215static __inline int 216iflr_cmp(const struct if_llreach *a, const struct if_llreach *b) 217{ 218 return (memcmp(&a->lr_key, &b->lr_key, sizeof (a->lr_key))); 219} 220 221static __inline int 222iflr_reachable(struct if_llreach *lr, int cmp_delta, u_int64_t tval) 223{ 224 u_int64_t now; 225 u_int64_t expire; 226 227 now = net_uptime(); /* current approx. uptime */ 228 /* 229 * No need for lr_lock; atomically read the last rcvd uptime. 230 */ 231 expire = lr->lr_lastrcvd + lr->lr_reachable; 232 /* 233 * If we haven't heard back from the local host for over 234 * lr_reachable seconds, consider that the host is no 235 * longer reachable. 236 */ 237 if (!cmp_delta) 238 return (expire >= now); 239 /* 240 * If the caller supplied a reference time, consider the 241 * host is reachable if the record hasn't expired (see above) 242 * and if the reference time is within the past lr_reachable 243 * seconds. 244 */ 245 return ((expire >= now) && (now - tval) < lr->lr_reachable); 246} 247 248int 249ifnet_llreach_reachable(struct if_llreach *lr) 250{ 251 /* 252 * Check whether the cache is too old to be trusted. 253 */ 254 return (iflr_reachable(lr, 0, 0)); 255} 256 257int 258ifnet_llreach_reachable_delta(struct if_llreach *lr, u_int64_t tval) 259{ 260 /* 261 * Check whether the cache is too old to be trusted. 262 */ 263 return (iflr_reachable(lr, 1, tval)); 264} 265 266void 267ifnet_llreach_set_reachable(struct ifnet *ifp, u_int16_t llproto, void *addr, 268 unsigned int alen) 269{ 270 struct if_llreach find, *lr; 271 272 VERIFY(alen == IF_LLREACH_MAXLEN); /* for now */ 273 274 find.lr_key.proto = llproto; 275 bcopy(addr, &find.lr_key.addr, IF_LLREACH_MAXLEN); 276 277 lck_rw_lock_shared(&ifp->if_llreach_lock); 278 lr = RB_FIND(ll_reach_tree, &ifp->if_ll_srcs, &find); 279 if (lr == NULL) { 280 lck_rw_done(&ifp->if_llreach_lock); 281 return; 282 } 283 /* 284 * No need for lr_lock; atomically update the last rcvd uptime. 285 */ 286 lr->lr_lastrcvd = net_uptime(); 287 lck_rw_done(&ifp->if_llreach_lock); 288} 289 290struct if_llreach * 291ifnet_llreach_alloc(struct ifnet *ifp, u_int16_t llproto, void *addr, 292 unsigned int alen, u_int64_t llreach_base) 293{ 294 struct if_llreach find, *lr; 295 struct timeval cnow; 296 297 if (llreach_base == 0) 298 return (NULL); 299 300 VERIFY(alen == IF_LLREACH_MAXLEN); /* for now */ 301 302 find.lr_key.proto = llproto; 303 bcopy(addr, &find.lr_key.addr, IF_LLREACH_MAXLEN); 304 305 lck_rw_lock_shared(&ifp->if_llreach_lock); 306 lr = RB_FIND(ll_reach_tree, &ifp->if_ll_srcs, &find); 307 if (lr != NULL) { 308found: 309 IFLR_LOCK(lr); 310 VERIFY(lr->lr_reqcnt >= 1); 311 lr->lr_reqcnt++; 312 VERIFY(lr->lr_reqcnt != 0); 313 IFLR_ADDREF_LOCKED(lr); /* for caller */ 314 lr->lr_lastrcvd = net_uptime(); /* current approx. uptime */ 315 IFLR_UNLOCK(lr); 316 lck_rw_done(&ifp->if_llreach_lock); 317 return (lr); 318 } 319 320 if (!lck_rw_lock_shared_to_exclusive(&ifp->if_llreach_lock)) 321 lck_rw_lock_exclusive(&ifp->if_llreach_lock); 322 323 lck_rw_assert(&ifp->if_llreach_lock, LCK_RW_ASSERT_EXCLUSIVE); 324 325 /* in case things have changed while becoming writer */ 326 lr = RB_FIND(ll_reach_tree, &ifp->if_ll_srcs, &find); 327 if (lr != NULL) 328 goto found; 329 330 lr = iflr_alloc(M_WAITOK); 331 if (lr == NULL) { 332 lck_rw_done(&ifp->if_llreach_lock); 333 return (NULL); 334 } 335 IFLR_LOCK(lr); 336 lr->lr_reqcnt++; 337 VERIFY(lr->lr_reqcnt == 1); 338 IFLR_ADDREF_LOCKED(lr); /* for RB tree */ 339 IFLR_ADDREF_LOCKED(lr); /* for caller */ 340 lr->lr_lastrcvd = net_uptime(); /* current approx. uptime */ 341 lr->lr_baseup = lr->lr_lastrcvd; /* base uptime */ 342 getmicrotime(&cnow); 343 lr->lr_basecal = cnow.tv_sec; /* base calendar time */ 344 lr->lr_basereachable = llreach_base; 345 lr->lr_reachable = LL_COMPUTE_RTIME(lr->lr_basereachable * 1000); 346 lr->lr_debug |= IFD_ATTACHED; 347 lr->lr_ifp = ifp; 348 lr->lr_key.proto = llproto; 349 bcopy(addr, &lr->lr_key.addr, IF_LLREACH_MAXLEN); 350 lr->lr_rssi = IFNET_RSSI_UNKNOWN; 351 lr->lr_lqm = IFNET_LQM_THRESH_UNKNOWN; 352 lr->lr_npm = IFNET_NPM_THRESH_UNKNOWN; 353 RB_INSERT(ll_reach_tree, &ifp->if_ll_srcs, lr); 354 IFLR_UNLOCK(lr); 355 lck_rw_done(&ifp->if_llreach_lock); 356 357 return (lr); 358} 359 360void 361ifnet_llreach_free(struct if_llreach *lr) 362{ 363 struct ifnet *ifp; 364 365 /* no need to lock here; lr_ifp never changes */ 366 ifp = lr->lr_ifp; 367 368 lck_rw_lock_exclusive(&ifp->if_llreach_lock); 369 IFLR_LOCK(lr); 370 if (lr->lr_reqcnt == 0) { 371 panic("%s: lr=%p negative reqcnt", __func__, lr); 372 /* NOTREACHED */ 373 } 374 --lr->lr_reqcnt; 375 if (lr->lr_reqcnt > 0) { 376 IFLR_UNLOCK(lr); 377 lck_rw_done(&ifp->if_llreach_lock); 378 IFLR_REMREF(lr); /* for caller */ 379 return; 380 } 381 if (!(lr->lr_debug & IFD_ATTACHED)) { 382 panic("%s: Attempt to detach an unattached llreach lr=%p", 383 __func__, lr); 384 /* NOTREACHED */ 385 } 386 lr->lr_debug &= ~IFD_ATTACHED; 387 RB_REMOVE(ll_reach_tree, &ifp->if_ll_srcs, lr); 388 IFLR_UNLOCK(lr); 389 lck_rw_done(&ifp->if_llreach_lock); 390 391 IFLR_REMREF(lr); /* for RB tree */ 392 IFLR_REMREF(lr); /* for caller */ 393} 394 395u_int64_t 396ifnet_llreach_up2calexp(struct if_llreach *lr, u_int64_t uptime) 397{ 398 u_int64_t calendar = 0; 399 400 if (uptime != 0) { 401 struct timeval cnow; 402 u_int64_t unow; 403 404 getmicrotime(&cnow); /* current calendar time */ 405 unow = net_uptime(); /* current approx. uptime */ 406 /* 407 * Take into account possible calendar time changes; 408 * adjust base calendar value if necessary, i.e. 409 * the calendar skew should equate to the uptime skew. 410 */ 411 lr->lr_basecal += (cnow.tv_sec - lr->lr_basecal) - 412 (unow - lr->lr_baseup); 413 414 calendar = lr->lr_basecal + lr->lr_reachable + 415 (uptime - lr->lr_baseup); 416 } 417 418 return (calendar); 419} 420 421u_int64_t 422ifnet_llreach_up2upexp(struct if_llreach *lr, u_int64_t uptime) 423{ 424 return (lr->lr_reachable + uptime); 425} 426 427int 428ifnet_llreach_get_defrouter(struct ifnet *ifp, int af, 429 struct ifnet_llreach_info *iflri) 430{ 431 struct radix_node_head *rnh; 432 struct sockaddr_storage dst_ss, mask_ss; 433 struct rtentry *rt; 434 int error = ESRCH; 435 436 VERIFY(ifp != NULL && iflri != NULL && 437 (af == AF_INET || af == AF_INET6)); 438 439 bzero(iflri, sizeof (*iflri)); 440 441 if ((rnh = rt_tables[af]) == NULL) 442 return (error); 443 444 bzero(&dst_ss, sizeof (dst_ss)); 445 bzero(&mask_ss, sizeof (mask_ss)); 446 dst_ss.ss_family = af; 447 dst_ss.ss_len = (af == AF_INET) ? sizeof (struct sockaddr_in) : 448 sizeof (struct sockaddr_in6); 449 450 lck_mtx_lock(rnh_lock); 451 rt = rt_lookup(TRUE, SA(&dst_ss), SA(&mask_ss), rnh, ifp->if_index); 452 if (rt != NULL) { 453 struct rtentry *gwrt; 454 455 RT_LOCK(rt); 456 if ((rt->rt_flags & RTF_GATEWAY) && 457 (gwrt = rt->rt_gwroute) != NULL && 458 rt_key(rt)->sa_family == rt_key(gwrt)->sa_family && 459 (gwrt->rt_flags & RTF_UP)) { 460 RT_UNLOCK(rt); 461 RT_LOCK(gwrt); 462 if (gwrt->rt_llinfo_get_iflri != NULL) { 463 (*gwrt->rt_llinfo_get_iflri)(gwrt, iflri); 464 error = 0; 465 } 466 RT_UNLOCK(gwrt); 467 } else { 468 RT_UNLOCK(rt); 469 } 470 rtfree_locked(rt); 471 } 472 lck_mtx_unlock(rnh_lock); 473 474 return (error); 475} 476 477static struct if_llreach * 478iflr_alloc(int how) 479{ 480 struct if_llreach *lr; 481 482 lr = (how == M_WAITOK) ? zalloc(iflr_zone) : zalloc_noblock(iflr_zone); 483 if (lr != NULL) { 484 bzero(lr, iflr_size); 485 lck_mtx_init(&lr->lr_lock, ifnet_lock_group, ifnet_lock_attr); 486 lr->lr_debug |= IFD_ALLOC; 487 } 488 return (lr); 489} 490 491static void 492iflr_free(struct if_llreach *lr) 493{ 494 IFLR_LOCK(lr); 495 if (lr->lr_debug & IFD_ATTACHED) { 496 panic("%s: attached lr=%p is being freed", __func__, lr); 497 /* NOTREACHED */ 498 } else if (!(lr->lr_debug & IFD_ALLOC)) { 499 panic("%s: lr %p cannot be freed", __func__, lr); 500 /* NOTREACHED */ 501 } else if (lr->lr_refcnt != 0) { 502 panic("%s: non-zero refcount lr=%p", __func__, lr); 503 /* NOTREACHED */ 504 } else if (lr->lr_reqcnt != 0) { 505 panic("%s: non-zero reqcnt lr=%p", __func__, lr); 506 /* NOTREACHED */ 507 } 508 lr->lr_debug &= ~IFD_ALLOC; 509 IFLR_UNLOCK(lr); 510 511 lck_mtx_destroy(&lr->lr_lock, ifnet_lock_group); 512 zfree(iflr_zone, lr); 513} 514 515void 516iflr_addref(struct if_llreach *lr, int locked) 517{ 518 if (!locked) 519 IFLR_LOCK(lr); 520 else 521 IFLR_LOCK_ASSERT_HELD(lr); 522 523 if (++lr->lr_refcnt == 0) { 524 panic("%s: lr=%p wraparound refcnt", __func__, lr); 525 /* NOTREACHED */ 526 } 527 if (!locked) 528 IFLR_UNLOCK(lr); 529} 530 531void 532iflr_remref(struct if_llreach *lr) 533{ 534 IFLR_LOCK(lr); 535 if (lr->lr_refcnt == 0) { 536 panic("%s: lr=%p negative refcnt", __func__, lr); 537 /* NOTREACHED */ 538 } 539 --lr->lr_refcnt; 540 if (lr->lr_refcnt > 0) { 541 IFLR_UNLOCK(lr); 542 return; 543 } 544 IFLR_UNLOCK(lr); 545 546 iflr_free(lr); /* deallocate it */ 547} 548 549void 550ifnet_lr2ri(struct if_llreach *lr, struct rt_reach_info *ri) 551{ 552 struct if_llreach_info lri; 553 554 IFLR_LOCK_ASSERT_HELD(lr); 555 556 bzero(ri, sizeof (*ri)); 557 ifnet_lr2lri(lr, &lri); 558 ri->ri_refcnt = lri.lri_refcnt; 559 ri->ri_probes = lri.lri_probes; 560 ri->ri_rcv_expire = lri.lri_expire; 561 ri->ri_rssi = lri.lri_rssi; 562 ri->ri_lqm = lri.lri_lqm; 563 ri->ri_npm = lri.lri_npm; 564} 565 566void 567ifnet_lr2iflri(struct if_llreach *lr, struct ifnet_llreach_info *iflri) 568{ 569 IFLR_LOCK_ASSERT_HELD(lr); 570 571 bzero(iflri, sizeof (*iflri)); 572 /* 573 * Note here we return request count, not actual memory refcnt. 574 */ 575 iflri->iflri_refcnt = lr->lr_reqcnt; 576 iflri->iflri_probes = lr->lr_probes; 577 iflri->iflri_rcv_expire = ifnet_llreach_up2upexp(lr, lr->lr_lastrcvd); 578 iflri->iflri_curtime = net_uptime(); 579 switch (lr->lr_key.proto) { 580 case ETHERTYPE_IP: 581 iflri->iflri_netproto = PF_INET; 582 break; 583 case ETHERTYPE_IPV6: 584 iflri->iflri_netproto = PF_INET6; 585 break; 586 default: 587 /* 588 * This shouldn't be possible for the time being, 589 * since link-layer reachability records are only 590 * kept for ARP and ND6. 591 */ 592 iflri->iflri_netproto = PF_UNSPEC; 593 break; 594 } 595 bcopy(&lr->lr_key.addr, &iflri->iflri_addr, IF_LLREACH_MAXLEN); 596 iflri->iflri_rssi = lr->lr_rssi; 597 iflri->iflri_lqm = lr->lr_lqm; 598 iflri->iflri_npm = lr->lr_npm; 599} 600 601void 602ifnet_lr2lri(struct if_llreach *lr, struct if_llreach_info *lri) 603{ 604 IFLR_LOCK_ASSERT_HELD(lr); 605 606 bzero(lri, sizeof (*lri)); 607 /* 608 * Note here we return request count, not actual memory refcnt. 609 */ 610 lri->lri_refcnt = lr->lr_reqcnt; 611 lri->lri_ifindex = lr->lr_ifp->if_index; 612 lri->lri_probes = lr->lr_probes; 613 lri->lri_expire = ifnet_llreach_up2calexp(lr, lr->lr_lastrcvd); 614 lri->lri_proto = lr->lr_key.proto; 615 bcopy(&lr->lr_key.addr, &lri->lri_addr, IF_LLREACH_MAXLEN); 616 lri->lri_rssi = lr->lr_rssi; 617 lri->lri_lqm = lr->lr_lqm; 618 lri->lri_npm = lr->lr_npm; 619} 620 621static int 622sysctl_llreach_ifinfo SYSCTL_HANDLER_ARGS 623{ 624#pragma unused(oidp) 625 int *name, retval = 0; 626 unsigned int namelen; 627 uint32_t ifindex; 628 struct if_llreach *lr; 629 struct if_llreach_info lri; 630 struct ifnet *ifp; 631 632 name = (int *)arg1; 633 namelen = (unsigned int)arg2; 634 635 if (req->newptr != USER_ADDR_NULL) 636 return (EPERM); 637 638 if (namelen != 1) 639 return (EINVAL); 640 641 ifindex = name[0]; 642 ifnet_head_lock_shared(); 643 if (ifindex <= 0 || ifindex > (u_int)if_index) { 644 printf("%s: ifindex %u out of range\n", __func__, ifindex); 645 ifnet_head_done(); 646 return (ENOENT); 647 } 648 649 ifp = ifindex2ifnet[ifindex]; 650 ifnet_head_done(); 651 if (ifp == NULL) { 652 printf("%s: no ifp for ifindex %u\n", __func__, ifindex); 653 return (ENOENT); 654 } 655 656 lck_rw_lock_shared(&ifp->if_llreach_lock); 657 RB_FOREACH(lr, ll_reach_tree, &ifp->if_ll_srcs) { 658 /* Export to if_llreach_info structure */ 659 IFLR_LOCK(lr); 660 ifnet_lr2lri(lr, &lri); 661 IFLR_UNLOCK(lr); 662 663 if ((retval = SYSCTL_OUT(req, &lri, sizeof (lri))) != 0) 664 break; 665 } 666 lck_rw_done(&ifp->if_llreach_lock); 667 668 return (retval); 669} 670