]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/if_llreach.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / bsd / net / if_llreach.c
CommitLineData
6d2010ae 1/*
316670eb 2 * Copyright (c) 2011-2012 Apple Inc. All rights reserved.
6d2010ae
A
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
39236c6e
A
120#include <dev/random/randomdev.h>
121
6d2010ae
A
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>
316670eb
A
127#include <net/kpi_interface.h>
128#include <net/route.h>
6d2010ae
A
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
0a7de745
A
139static unsigned int iflr_size; /* size of if_llreach */
140static struct zone *iflr_zone; /* zone for if_llreach */
6d2010ae 141
0a7de745
A
142#define IFLR_ZONE_MAX 128 /* maximum elements in zone */
143#define IFLR_ZONE_NAME "if_llreach" /* zone name */
6d2010ae
A
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
0a7de745 165#define LL_COMPUTE_RTIME(x) ND_COMPUTE_RTIME(x)
6d2010ae 166#else
0a7de745
A
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() & \
6d2010ae
A
171 ((LL_MAX_RANDOM_FACTOR - LL_MIN_RANDOM_FACTOR) * (x >> 10)))) / 1000)
172#endif /* !INET6 */
173
174void
175ifnet_llreach_init(void)
176{
0a7de745 177 iflr_size = sizeof(struct if_llreach);
6d2010ae
A
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) */
0a7de745 193 if (!reuse) {
6d2010ae 194 RB_INIT(&ifp->if_ll_srcs);
0a7de745 195 }
6d2010ae
A
196 lck_rw_done(&ifp->if_llreach_lock);
197}
198
199void
200ifnet_llreach_ifdetach(struct ifnet *ifp)
201{
202#pragma unused(ifp)
203 /*
204 * Nothing to do for now; the link-layer source tree might
205 * contain entries at this point, that are still referred
206 * to by route entries pointing to this ifp.
207 */
208}
209
210/*
211 * Link-layer source tree comparison function.
212 *
213 * An ordered predicate is necessary; bcmp() is not documented to return
214 * an indication of order, memcmp() is, and is an ISO C99 requirement.
215 */
216static __inline int
217iflr_cmp(const struct if_llreach *a, const struct if_llreach *b)
218{
0a7de745 219 return memcmp(&a->lr_key, &b->lr_key, sizeof(a->lr_key));
6d2010ae
A
220}
221
222static __inline int
223iflr_reachable(struct if_llreach *lr, int cmp_delta, u_int64_t tval)
224{
225 u_int64_t now;
226 u_int64_t expire;
227
0a7de745 228 now = net_uptime(); /* current approx. uptime */
6d2010ae
A
229 /*
230 * No need for lr_lock; atomically read the last rcvd uptime.
231 */
232 expire = lr->lr_lastrcvd + lr->lr_reachable;
233 /*
234 * If we haven't heard back from the local host for over
235 * lr_reachable seconds, consider that the host is no
236 * longer reachable.
237 */
0a7de745
A
238 if (!cmp_delta) {
239 return expire >= now;
240 }
6d2010ae
A
241 /*
242 * If the caller supplied a reference time, consider the
243 * host is reachable if the record hasn't expired (see above)
244 * and if the reference time is within the past lr_reachable
245 * seconds.
246 */
0a7de745 247 return (expire >= now) && (now - tval) < lr->lr_reachable;
6d2010ae
A
248}
249
250int
251ifnet_llreach_reachable(struct if_llreach *lr)
252{
253 /*
254 * Check whether the cache is too old to be trusted.
255 */
0a7de745 256 return iflr_reachable(lr, 0, 0);
6d2010ae
A
257}
258
259int
260ifnet_llreach_reachable_delta(struct if_llreach *lr, u_int64_t tval)
261{
262 /*
263 * Check whether the cache is too old to be trusted.
264 */
0a7de745 265 return iflr_reachable(lr, 1, tval);
6d2010ae
A
266}
267
268void
269ifnet_llreach_set_reachable(struct ifnet *ifp, u_int16_t llproto, void *addr,
270 unsigned int alen)
271{
272 struct if_llreach find, *lr;
273
0a7de745 274 VERIFY(alen == IF_LLREACH_MAXLEN); /* for now */
6d2010ae
A
275
276 find.lr_key.proto = llproto;
277 bcopy(addr, &find.lr_key.addr, IF_LLREACH_MAXLEN);
278
279 lck_rw_lock_shared(&ifp->if_llreach_lock);
280 lr = RB_FIND(ll_reach_tree, &ifp->if_ll_srcs, &find);
281 if (lr == NULL) {
282 lck_rw_done(&ifp->if_llreach_lock);
283 return;
284 }
285 /*
286 * No need for lr_lock; atomically update the last rcvd uptime.
287 */
288 lr->lr_lastrcvd = net_uptime();
289 lck_rw_done(&ifp->if_llreach_lock);
290}
291
292struct if_llreach *
293ifnet_llreach_alloc(struct ifnet *ifp, u_int16_t llproto, void *addr,
294 unsigned int alen, u_int64_t llreach_base)
295{
296 struct if_llreach find, *lr;
39236c6e 297 struct timeval cnow;
6d2010ae 298
0a7de745
A
299 if (llreach_base == 0) {
300 return NULL;
301 }
6d2010ae 302
0a7de745 303 VERIFY(alen == IF_LLREACH_MAXLEN); /* for now */
6d2010ae
A
304
305 find.lr_key.proto = llproto;
306 bcopy(addr, &find.lr_key.addr, IF_LLREACH_MAXLEN);
307
308 lck_rw_lock_shared(&ifp->if_llreach_lock);
309 lr = RB_FIND(ll_reach_tree, &ifp->if_ll_srcs, &find);
310 if (lr != NULL) {
311found:
312 IFLR_LOCK(lr);
313 VERIFY(lr->lr_reqcnt >= 1);
314 lr->lr_reqcnt++;
315 VERIFY(lr->lr_reqcnt != 0);
0a7de745
A
316 IFLR_ADDREF_LOCKED(lr); /* for caller */
317 lr->lr_lastrcvd = net_uptime(); /* current approx. uptime */
6d2010ae
A
318 IFLR_UNLOCK(lr);
319 lck_rw_done(&ifp->if_llreach_lock);
0a7de745 320 return lr;
6d2010ae
A
321 }
322
0a7de745 323 if (!lck_rw_lock_shared_to_exclusive(&ifp->if_llreach_lock)) {
6d2010ae 324 lck_rw_lock_exclusive(&ifp->if_llreach_lock);
0a7de745 325 }
6d2010ae 326
5ba3f43e 327 LCK_RW_ASSERT(&ifp->if_llreach_lock, LCK_RW_ASSERT_EXCLUSIVE);
6d2010ae
A
328
329 /* in case things have changed while becoming writer */
330 lr = RB_FIND(ll_reach_tree, &ifp->if_ll_srcs, &find);
0a7de745 331 if (lr != NULL) {
6d2010ae 332 goto found;
0a7de745 333 }
6d2010ae
A
334
335 lr = iflr_alloc(M_WAITOK);
336 if (lr == NULL) {
337 lck_rw_done(&ifp->if_llreach_lock);
0a7de745 338 return NULL;
6d2010ae
A
339 }
340 IFLR_LOCK(lr);
341 lr->lr_reqcnt++;
342 VERIFY(lr->lr_reqcnt == 1);
0a7de745
A
343 IFLR_ADDREF_LOCKED(lr); /* for RB tree */
344 IFLR_ADDREF_LOCKED(lr); /* for caller */
345 lr->lr_lastrcvd = net_uptime(); /* current approx. uptime */
346 lr->lr_baseup = lr->lr_lastrcvd; /* base uptime */
39236c6e 347 getmicrotime(&cnow);
0a7de745 348 lr->lr_basecal = cnow.tv_sec; /* base calendar time */
6d2010ae
A
349 lr->lr_basereachable = llreach_base;
350 lr->lr_reachable = LL_COMPUTE_RTIME(lr->lr_basereachable * 1000);
351 lr->lr_debug |= IFD_ATTACHED;
352 lr->lr_ifp = ifp;
353 lr->lr_key.proto = llproto;
354 bcopy(addr, &lr->lr_key.addr, IF_LLREACH_MAXLEN);
316670eb
A
355 lr->lr_rssi = IFNET_RSSI_UNKNOWN;
356 lr->lr_lqm = IFNET_LQM_THRESH_UNKNOWN;
357 lr->lr_npm = IFNET_NPM_THRESH_UNKNOWN;
6d2010ae
A
358 RB_INSERT(ll_reach_tree, &ifp->if_ll_srcs, lr);
359 IFLR_UNLOCK(lr);
360 lck_rw_done(&ifp->if_llreach_lock);
361
0a7de745 362 return lr;
6d2010ae
A
363}
364
365void
366ifnet_llreach_free(struct if_llreach *lr)
367{
368 struct ifnet *ifp;
369
370 /* no need to lock here; lr_ifp never changes */
371 ifp = lr->lr_ifp;
372
373 lck_rw_lock_exclusive(&ifp->if_llreach_lock);
374 IFLR_LOCK(lr);
375 if (lr->lr_reqcnt == 0) {
376 panic("%s: lr=%p negative reqcnt", __func__, lr);
377 /* NOTREACHED */
378 }
379 --lr->lr_reqcnt;
380 if (lr->lr_reqcnt > 0) {
381 IFLR_UNLOCK(lr);
382 lck_rw_done(&ifp->if_llreach_lock);
0a7de745 383 IFLR_REMREF(lr); /* for caller */
6d2010ae
A
384 return;
385 }
386 if (!(lr->lr_debug & IFD_ATTACHED)) {
387 panic("%s: Attempt to detach an unattached llreach lr=%p",
388 __func__, lr);
389 /* NOTREACHED */
390 }
391 lr->lr_debug &= ~IFD_ATTACHED;
392 RB_REMOVE(ll_reach_tree, &ifp->if_ll_srcs, lr);
393 IFLR_UNLOCK(lr);
394 lck_rw_done(&ifp->if_llreach_lock);
395
0a7de745
A
396 IFLR_REMREF(lr); /* for RB tree */
397 IFLR_REMREF(lr); /* for caller */
6d2010ae
A
398}
399
400u_int64_t
316670eb 401ifnet_llreach_up2calexp(struct if_llreach *lr, u_int64_t uptime)
6d2010ae
A
402{
403 u_int64_t calendar = 0;
404
405 if (uptime != 0) {
406 struct timeval cnow;
407 u_int64_t unow;
408
0a7de745
A
409 getmicrotime(&cnow); /* current calendar time */
410 unow = net_uptime(); /* current approx. uptime */
6d2010ae
A
411 /*
412 * Take into account possible calendar time changes;
413 * adjust base calendar value if necessary, i.e.
414 * the calendar skew should equate to the uptime skew.
415 */
416 lr->lr_basecal += (cnow.tv_sec - lr->lr_basecal) -
417 (unow - lr->lr_baseup);
418
419 calendar = lr->lr_basecal + lr->lr_reachable +
420 (uptime - lr->lr_baseup);
421 }
422
0a7de745 423 return calendar;
6d2010ae
A
424}
425
316670eb
A
426u_int64_t
427ifnet_llreach_up2upexp(struct if_llreach *lr, u_int64_t uptime)
428{
0a7de745 429 return lr->lr_reachable + uptime;
316670eb
A
430}
431
432int
433ifnet_llreach_get_defrouter(struct ifnet *ifp, int af,
434 struct ifnet_llreach_info *iflri)
435{
436 struct radix_node_head *rnh;
437 struct sockaddr_storage dst_ss, mask_ss;
438 struct rtentry *rt;
439 int error = ESRCH;
440
441 VERIFY(ifp != NULL && iflri != NULL &&
442 (af == AF_INET || af == AF_INET6));
443
0a7de745 444 bzero(iflri, sizeof(*iflri));
316670eb 445
0a7de745
A
446 if ((rnh = rt_tables[af]) == NULL) {
447 return error;
448 }
316670eb 449
0a7de745
A
450 bzero(&dst_ss, sizeof(dst_ss));
451 bzero(&mask_ss, sizeof(mask_ss));
316670eb 452 dst_ss.ss_family = af;
0a7de745
A
453 dst_ss.ss_len = (af == AF_INET) ? sizeof(struct sockaddr_in) :
454 sizeof(struct sockaddr_in6);
316670eb
A
455
456 lck_mtx_lock(rnh_lock);
457 rt = rt_lookup(TRUE, SA(&dst_ss), SA(&mask_ss), rnh, ifp->if_index);
458 if (rt != NULL) {
459 struct rtentry *gwrt;
460
461 RT_LOCK(rt);
462 if ((rt->rt_flags & RTF_GATEWAY) &&
463 (gwrt = rt->rt_gwroute) != NULL &&
464 rt_key(rt)->sa_family == rt_key(gwrt)->sa_family &&
465 (gwrt->rt_flags & RTF_UP)) {
466 RT_UNLOCK(rt);
467 RT_LOCK(gwrt);
468 if (gwrt->rt_llinfo_get_iflri != NULL) {
469 (*gwrt->rt_llinfo_get_iflri)(gwrt, iflri);
470 error = 0;
471 }
472 RT_UNLOCK(gwrt);
473 } else {
474 RT_UNLOCK(rt);
475 }
476 rtfree_locked(rt);
477 }
478 lck_mtx_unlock(rnh_lock);
479
0a7de745 480 return error;
316670eb
A
481}
482
6d2010ae
A
483static struct if_llreach *
484iflr_alloc(int how)
485{
486 struct if_llreach *lr;
487
488 lr = (how == M_WAITOK) ? zalloc(iflr_zone) : zalloc_noblock(iflr_zone);
489 if (lr != NULL) {
490 bzero(lr, iflr_size);
491 lck_mtx_init(&lr->lr_lock, ifnet_lock_group, ifnet_lock_attr);
492 lr->lr_debug |= IFD_ALLOC;
493 }
0a7de745 494 return lr;
6d2010ae
A
495}
496
497static void
498iflr_free(struct if_llreach *lr)
499{
500 IFLR_LOCK(lr);
501 if (lr->lr_debug & IFD_ATTACHED) {
502 panic("%s: attached lr=%p is being freed", __func__, lr);
503 /* NOTREACHED */
504 } else if (!(lr->lr_debug & IFD_ALLOC)) {
505 panic("%s: lr %p cannot be freed", __func__, lr);
506 /* NOTREACHED */
507 } else if (lr->lr_refcnt != 0) {
508 panic("%s: non-zero refcount lr=%p", __func__, lr);
509 /* NOTREACHED */
510 } else if (lr->lr_reqcnt != 0) {
511 panic("%s: non-zero reqcnt lr=%p", __func__, lr);
512 /* NOTREACHED */
513 }
514 lr->lr_debug &= ~IFD_ALLOC;
515 IFLR_UNLOCK(lr);
516
517 lck_mtx_destroy(&lr->lr_lock, ifnet_lock_group);
518 zfree(iflr_zone, lr);
519}
520
521void
522iflr_addref(struct if_llreach *lr, int locked)
523{
0a7de745 524 if (!locked) {
6d2010ae 525 IFLR_LOCK(lr);
0a7de745 526 } else {
6d2010ae 527 IFLR_LOCK_ASSERT_HELD(lr);
0a7de745 528 }
6d2010ae
A
529
530 if (++lr->lr_refcnt == 0) {
531 panic("%s: lr=%p wraparound refcnt", __func__, lr);
532 /* NOTREACHED */
533 }
0a7de745 534 if (!locked) {
6d2010ae 535 IFLR_UNLOCK(lr);
0a7de745 536 }
6d2010ae
A
537}
538
539void
540iflr_remref(struct if_llreach *lr)
541{
542 IFLR_LOCK(lr);
543 if (lr->lr_refcnt == 0) {
544 panic("%s: lr=%p negative refcnt", __func__, lr);
545 /* NOTREACHED */
546 }
547 --lr->lr_refcnt;
548 if (lr->lr_refcnt > 0) {
549 IFLR_UNLOCK(lr);
550 return;
551 }
552 IFLR_UNLOCK(lr);
553
0a7de745 554 iflr_free(lr); /* deallocate it */
6d2010ae
A
555}
556
557void
558ifnet_lr2ri(struct if_llreach *lr, struct rt_reach_info *ri)
559{
560 struct if_llreach_info lri;
561
562 IFLR_LOCK_ASSERT_HELD(lr);
563
0a7de745 564 bzero(ri, sizeof(*ri));
6d2010ae
A
565 ifnet_lr2lri(lr, &lri);
566 ri->ri_refcnt = lri.lri_refcnt;
567 ri->ri_probes = lri.lri_probes;
568 ri->ri_rcv_expire = lri.lri_expire;
316670eb
A
569 ri->ri_rssi = lri.lri_rssi;
570 ri->ri_lqm = lri.lri_lqm;
571 ri->ri_npm = lri.lri_npm;
572}
573
574void
575ifnet_lr2iflri(struct if_llreach *lr, struct ifnet_llreach_info *iflri)
576{
577 IFLR_LOCK_ASSERT_HELD(lr);
578
0a7de745 579 bzero(iflri, sizeof(*iflri));
316670eb
A
580 /*
581 * Note here we return request count, not actual memory refcnt.
582 */
583 iflri->iflri_refcnt = lr->lr_reqcnt;
584 iflri->iflri_probes = lr->lr_probes;
585 iflri->iflri_rcv_expire = ifnet_llreach_up2upexp(lr, lr->lr_lastrcvd);
586 iflri->iflri_curtime = net_uptime();
587 switch (lr->lr_key.proto) {
588 case ETHERTYPE_IP:
589 iflri->iflri_netproto = PF_INET;
590 break;
591 case ETHERTYPE_IPV6:
592 iflri->iflri_netproto = PF_INET6;
593 break;
594 default:
595 /*
596 * This shouldn't be possible for the time being,
597 * since link-layer reachability records are only
598 * kept for ARP and ND6.
599 */
600 iflri->iflri_netproto = PF_UNSPEC;
601 break;
602 }
603 bcopy(&lr->lr_key.addr, &iflri->iflri_addr, IF_LLREACH_MAXLEN);
604 iflri->iflri_rssi = lr->lr_rssi;
605 iflri->iflri_lqm = lr->lr_lqm;
606 iflri->iflri_npm = lr->lr_npm;
6d2010ae
A
607}
608
609void
610ifnet_lr2lri(struct if_llreach *lr, struct if_llreach_info *lri)
611{
612 IFLR_LOCK_ASSERT_HELD(lr);
613
0a7de745 614 bzero(lri, sizeof(*lri));
6d2010ae
A
615 /*
616 * Note here we return request count, not actual memory refcnt.
617 */
0a7de745 618 lri->lri_refcnt = lr->lr_reqcnt;
6d2010ae 619 lri->lri_ifindex = lr->lr_ifp->if_index;
0a7de745 620 lri->lri_probes = lr->lr_probes;
316670eb 621 lri->lri_expire = ifnet_llreach_up2calexp(lr, lr->lr_lastrcvd);
6d2010ae
A
622 lri->lri_proto = lr->lr_key.proto;
623 bcopy(&lr->lr_key.addr, &lri->lri_addr, IF_LLREACH_MAXLEN);
316670eb
A
624 lri->lri_rssi = lr->lr_rssi;
625 lri->lri_lqm = lr->lr_lqm;
626 lri->lri_npm = lr->lr_npm;
6d2010ae
A
627}
628
629static int
630sysctl_llreach_ifinfo SYSCTL_HANDLER_ARGS
631{
632#pragma unused(oidp)
0a7de745
A
633 int *name, retval = 0;
634 unsigned int namelen;
635 uint32_t ifindex;
6d2010ae 636 struct if_llreach *lr;
527f9951 637 struct if_llreach_info lri = {};
0a7de745 638 struct ifnet *ifp;
6d2010ae
A
639
640 name = (int *)arg1;
641 namelen = (unsigned int)arg2;
642
0a7de745
A
643 if (req->newptr != USER_ADDR_NULL) {
644 return EPERM;
645 }
6d2010ae 646
0a7de745
A
647 if (namelen != 1) {
648 return EINVAL;
649 }
6d2010ae
A
650
651 ifindex = name[0];
652 ifnet_head_lock_shared();
653 if (ifindex <= 0 || ifindex > (u_int)if_index) {
654 printf("%s: ifindex %u out of range\n", __func__, ifindex);
655 ifnet_head_done();
0a7de745 656 return ENOENT;
6d2010ae
A
657 }
658
659 ifp = ifindex2ifnet[ifindex];
660 ifnet_head_done();
661 if (ifp == NULL) {
662 printf("%s: no ifp for ifindex %u\n", __func__, ifindex);
0a7de745 663 return ENOENT;
6d2010ae
A
664 }
665
666 lck_rw_lock_shared(&ifp->if_llreach_lock);
667 RB_FOREACH(lr, ll_reach_tree, &ifp->if_ll_srcs) {
668 /* Export to if_llreach_info structure */
669 IFLR_LOCK(lr);
670 ifnet_lr2lri(lr, &lri);
671 IFLR_UNLOCK(lr);
672
0a7de745 673 if ((retval = SYSCTL_OUT(req, &lri, sizeof(lri))) != 0) {
6d2010ae 674 break;
0a7de745 675 }
6d2010ae
A
676 }
677 lck_rw_done(&ifp->if_llreach_lock);
678
0a7de745 679 return retval;
6d2010ae 680}