2 * Copyright (c) 2011-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * Prefix-based Neighbor Discovery Proxy
32 * When an interface is marked with the ND6_IFF_PROXY_PREFIXES flag, all
33 * of current and future non-scoped on-link prefixes configured on the
34 * interface will be shared with the scoped variant of such prefixes on
35 * other interfaces. This allows for one or more prefixes to be shared
36 * across multiple links, with full support for Duplicate Addres Detection,
37 * Address Resolution and Neighbor Unreachability Detection.
39 * A non-scoped prefix may be configured statically, or dynamically via
40 * Router Advertisement. An interface is said to be an "upstream" interface
41 * when it is marked with ND6_IFF_PROXY_PREFIXES and has at least one prefix
42 * that is non-scoped (global, not scoped.) Such prefixes are marked with
43 * the NDPRF_PRPROXY flag.
45 * A scoped prefix typically gets configured by way of adding an address
46 * to a "downstream" interface, when the added address is part of an existing
47 * prefix that is allowed to be shared (i.e. NDPRF_PRPROXY prefixes.) Unlike
48 * non-scoped prefixes, however, scoped prefixes will never be marked with
49 * the NDPRF_PRPROXY flag.
51 * The setting of NDPRF_PRPROXY depends on whether the prefix is on-link;
52 * an off-link prefix on an interface marked with ND6_IFF_PROXY_PREFIXES
53 * will not cause NDPRF_PRPROXY to be set (it will only happen when that
54 * prefix goes on-link.) Likewise, a previously on-link prefix that has
55 * transitioned to off-link will cause its NDPRF_PRPROXY flag to be cleared.
57 * Prefix proxying relies on IPv6 Scoped Routing to be in effect, as it would
58 * otherwise be impossible to install scoped prefix route entries in the
59 * routing table. By default, such cloning prefix routes will generate cloned
60 * routes that are scoped according to their interfaces. Because prefix
61 * proxying is essentially creating a larger network comprised of multiple
62 * links sharing a prefix, we need to treat the cloned routes as if they
63 * weren't scoped route entries. This requires marking such cloning prefix
64 * routes with the RTF_PROXY flag, which serves as an indication that the
65 * route entry (and its clones) are part of a proxied prefix, and that the
66 * entries are non-scoped.
68 * In order to handle solicited-node destined ND packets (Address Resolution,
69 * Neighbor Unreachability Detection), prefix proxying also requires that the
70 * "upstream" and "downstream" interfaces be configured for all-multicast mode.
72 * The setting and clearing of RTF_PROXY flag, as well as the entering and
73 * exiting of all-multicast mode on those interfaces happen when a prefix
74 * transitions between on-link and off-link (vice versa.)
76 * Note that this is not a strict implementation of RFC 4389, but rather a
77 * derivative based on similar concept. In particular, we only proxy NS and
78 * NA packets; RA packets are never proxied. Care should be taken to enable
79 * prefix proxying only on non-looping network topology.
82 #include <sys/param.h>
83 #include <sys/systm.h>
84 #include <sys/malloc.h>
86 #include <sys/errno.h>
87 #include <sys/syslog.h>
88 #include <sys/sysctl.h>
89 #include <sys/mcache.h>
90 #include <sys/protosw.h>
92 #include <kern/queue.h>
93 #include <kern/zalloc.h>
96 #include <net/if_var.h>
97 #include <net/if_types.h>
98 #include <net/route.h>
100 #include <netinet/in.h>
101 #include <netinet/in_var.h>
102 #include <netinet6/in6_var.h>
103 #include <netinet/ip6.h>
104 #include <netinet6/ip6_var.h>
105 #include <netinet/icmp6.h>
106 #include <netinet6/nd6.h>
107 #include <netinet6/scope6_var.h>
109 struct nd6_prproxy_prelist
{
110 SLIST_ENTRY(nd6_prproxy_prelist
) ndprl_le
;
111 struct nd_prefix
*ndprl_pr
; /* prefix */
112 struct nd_prefix
*ndprl_up
; /* non-NULL for upstream */
113 struct ifnet
*ndprl_fwd_ifp
; /* outgoing interface */
114 boolean_t ndprl_sol
; /* unicast solicitor? */
115 struct in6_addr ndprl_sol_saddr
; /* solicitor's address */
119 * Soliciting node (source) record.
121 struct nd6_prproxy_solsrc
{
122 TAILQ_ENTRY(nd6_prproxy_solsrc
) solsrc_tqe
;
123 struct in6_addr solsrc_saddr
; /* soliciting (src) address */
124 struct ifnet
*solsrc_ifp
; /* iface where NS arrived on */
128 * Solicited node (target) record.
130 struct nd6_prproxy_soltgt
{
131 RB_ENTRY(nd6_prproxy_soltgt
) soltgt_link
; /* RB tree links */
132 struct soltgt_key_s
{
133 struct in6_addr taddr
; /* solicited (tgt) address */
135 u_int64_t soltgt_expire
; /* expiration time */
136 u_int32_t soltgt_cnt
; /* total # of solicitors */
137 TAILQ_HEAD(, nd6_prproxy_solsrc
) soltgt_q
;
140 SLIST_HEAD(nd6_prproxy_prelist_head
, nd6_prproxy_prelist
);
142 static void nd6_prproxy_prelist_setroute(boolean_t enable
,
143 struct nd6_prproxy_prelist_head
*, struct nd6_prproxy_prelist_head
*);
144 static struct nd6_prproxy_prelist
*nd6_ndprl_alloc(int);
145 static void nd6_ndprl_free(struct nd6_prproxy_prelist
*);
146 static struct nd6_prproxy_solsrc
*nd6_solsrc_alloc(int);
147 static void nd6_solsrc_free(struct nd6_prproxy_solsrc
*);
148 static boolean_t
nd6_solsrc_enq(struct nd_prefix
*, struct ifnet
*,
149 struct in6_addr
*, struct in6_addr
*);
150 static boolean_t
nd6_solsrc_deq(struct nd_prefix
*, struct in6_addr
*,
151 struct in6_addr
*, struct ifnet
**);
152 static struct nd6_prproxy_soltgt
*nd6_soltgt_alloc(int);
153 static void nd6_soltgt_free(struct nd6_prproxy_soltgt
*);
154 static void nd6_soltgt_prune(struct nd6_prproxy_soltgt
*, u_int32_t
);
155 static __inline
int soltgt_cmp(const struct nd6_prproxy_soltgt
*,
156 const struct nd6_prproxy_soltgt
*);
157 static void nd6_prproxy_sols_purge(struct nd_prefix
*, u_int64_t
);
159 RB_PROTOTYPE_SC_PREV(__private_extern__
, prproxy_sols_tree
, nd6_prproxy_soltgt
,
160 soltgt_link
, soltgt_cmp
);
163 * Time (in seconds) before a target record expires (is idle).
165 #define ND6_TGT_SOLS_EXPIRE 5
168 * Maximum number of queued soliciting (source) records per target.
170 #define ND6_MAX_SRC_SOLS_DEFAULT 4
173 * Maximum number of queued solicited (target) records per prefix.
175 #define ND6_MAX_TGT_SOLS_DEFAULT 8
177 static u_int32_t nd6_max_tgt_sols
= ND6_MAX_TGT_SOLS_DEFAULT
;
178 static u_int32_t nd6_max_src_sols
= ND6_MAX_SRC_SOLS_DEFAULT
;
180 static unsigned int ndprl_size
; /* size of zone element */
181 static struct zone
*ndprl_zone
; /* nd6_prproxy_prelist zone */
183 #define NDPRL_ZONE_MAX 256 /* maximum elements in zone */
184 #define NDPRL_ZONE_NAME "nd6_prproxy_prelist" /* name for zone */
186 static unsigned int solsrc_size
; /* size of zone element */
187 static struct zone
*solsrc_zone
; /* nd6_prproxy_solsrc zone */
189 #define SOLSRC_ZONE_MAX 256 /* maximum elements in zone */
190 #define SOLSRC_ZONE_NAME "nd6_prproxy_solsrc" /* name for zone */
192 static unsigned int soltgt_size
; /* size of zone element */
193 static struct zone
*soltgt_zone
; /* nd6_prproxy_soltgt zone */
195 #define SOLTGT_ZONE_MAX 256 /* maximum elements in zone */
196 #define SOLTGT_ZONE_NAME "nd6_prproxy_soltgt" /* name for zone */
198 /* The following is protected by ndpr_lock */
199 RB_GENERATE_PREV(prproxy_sols_tree
, nd6_prproxy_soltgt
,
200 soltgt_link
, soltgt_cmp
);
202 /* The following is protected by proxy6_lock (for updates) */
203 u_int32_t nd6_prproxy
;
205 extern lck_mtx_t
*nd6_mutex
;
207 SYSCTL_DECL(_net_inet6_icmp6
);
209 SYSCTL_UINT(_net_inet6_icmp6
, OID_AUTO
, nd6_maxsolstgt
,
210 CTLFLAG_RW
| CTLFLAG_LOCKED
, &nd6_max_tgt_sols
, ND6_MAX_TGT_SOLS_DEFAULT
,
211 "maximum number of outstanding solicited targets per prefix");
213 SYSCTL_UINT(_net_inet6_icmp6
, OID_AUTO
, nd6_maxproxiedsol
,
214 CTLFLAG_RW
| CTLFLAG_LOCKED
, &nd6_max_src_sols
, ND6_MAX_SRC_SOLS_DEFAULT
,
215 "maximum number of outstanding solicitations per target");
217 SYSCTL_UINT(_net_inet6_icmp6
, OID_AUTO
, prproxy_cnt
,
218 CTLFLAG_RD
| CTLFLAG_LOCKED
, &nd6_prproxy
, 0,
219 "total number of proxied prefixes");
222 * Called by nd6_init() during initialization time.
225 nd6_prproxy_init(void)
227 ndprl_size
= sizeof(struct nd6_prproxy_prelist
);
228 ndprl_zone
= zinit(ndprl_size
, NDPRL_ZONE_MAX
* ndprl_size
, 0,
230 if (ndprl_zone
== NULL
) {
231 panic("%s: failed allocating ndprl_zone", __func__
);
234 zone_change(ndprl_zone
, Z_EXPAND
, TRUE
);
235 zone_change(ndprl_zone
, Z_CALLERACCT
, FALSE
);
237 solsrc_size
= sizeof(struct nd6_prproxy_solsrc
);
238 solsrc_zone
= zinit(solsrc_size
, SOLSRC_ZONE_MAX
* solsrc_size
, 0,
240 if (solsrc_zone
== NULL
) {
241 panic("%s: failed allocating solsrc_zone", __func__
);
244 zone_change(solsrc_zone
, Z_EXPAND
, TRUE
);
245 zone_change(solsrc_zone
, Z_CALLERACCT
, FALSE
);
247 soltgt_size
= sizeof(struct nd6_prproxy_soltgt
);
248 soltgt_zone
= zinit(soltgt_size
, SOLTGT_ZONE_MAX
* soltgt_size
, 0,
250 if (soltgt_zone
== NULL
) {
251 panic("%s: failed allocating soltgt_zone", __func__
);
254 zone_change(soltgt_zone
, Z_EXPAND
, TRUE
);
255 zone_change(soltgt_zone
, Z_CALLERACCT
, FALSE
);
258 static struct nd6_prproxy_prelist
*
259 nd6_ndprl_alloc(int how
)
261 struct nd6_prproxy_prelist
*ndprl
;
263 ndprl
= (how
== M_WAITOK
) ? zalloc(ndprl_zone
) :
264 zalloc_noblock(ndprl_zone
);
266 bzero(ndprl
, ndprl_size
);
273 nd6_ndprl_free(struct nd6_prproxy_prelist
*ndprl
)
275 zfree(ndprl_zone
, ndprl
);
279 * Apply routing function on the affected upstream and downstream prefixes,
280 * i.e. either set or clear RTF_PROXY on the cloning prefix route; all route
281 * entries that were cloned off these prefixes will be blown away. Caller
282 * must have acquired proxy6_lock and must not be holding nd6_mutex.
285 nd6_prproxy_prelist_setroute(boolean_t enable
,
286 struct nd6_prproxy_prelist_head
*up_head
,
287 struct nd6_prproxy_prelist_head
*down_head
)
289 struct nd6_prproxy_prelist
*up
, *down
, *ndprl_tmp
;
290 struct nd_prefix
*pr
;
292 LCK_MTX_ASSERT(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
293 LCK_MTX_ASSERT(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
295 SLIST_FOREACH_SAFE(up
, up_head
, ndprl_le
, ndprl_tmp
) {
297 boolean_t prproxy
, set_allmulti
= FALSE
;
298 int allmulti_sw
= FALSE
;
299 struct ifnet
*ifp
= NULL
;
301 SLIST_REMOVE(up_head
, up
, nd6_prproxy_prelist
, ndprl_le
);
303 VERIFY(up
->ndprl_up
== NULL
);
307 prproxy
= (pr
->ndpr_stateflags
& NDPRF_PRPROXY
);
308 VERIFY(!prproxy
|| ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
309 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
311 nd6_prproxy_sols_reap(pr
);
312 VERIFY(pr
->ndpr_prproxy_sols_cnt
== 0);
313 VERIFY(RB_EMPTY(&pr
->ndpr_prproxy_sols
));
315 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
317 pr
->ndpr_allmulti_cnt
++;
320 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
322 pr
->ndpr_allmulti_cnt
--;
327 if ((rt
= pr
->ndpr_rt
) != NULL
) {
328 if ((enable
&& prproxy
) || (!enable
&& !prproxy
)) {
338 /* Call the following ioctl after releasing NDPR lock */
339 if (set_allmulti
&& ifp
!= NULL
) {
340 if_allmulti(ifp
, allmulti_sw
);
346 rt_set_proxy(rt
, enable
);
352 SLIST_FOREACH_SAFE(down
, down_head
, ndprl_le
, ndprl_tmp
) {
353 struct nd_prefix
*pr_up
;
355 boolean_t prproxy
, set_allmulti
= FALSE
;
356 int allmulti_sw
= FALSE
;
357 struct ifnet
*ifp
= NULL
;
359 SLIST_REMOVE(down_head
, down
, nd6_prproxy_prelist
, ndprl_le
);
361 pr_up
= down
->ndprl_up
;
362 VERIFY(pr_up
!= NULL
);
366 prproxy
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
367 VERIFY(!prproxy
|| ((pr_up
->ndpr_stateflags
& NDPRF_ONLINK
) &&
368 !(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
372 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
373 pr
->ndpr_allmulti_cnt
++;
376 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
377 pr
->ndpr_allmulti_cnt
--;
382 if ((rt
= pr
->ndpr_rt
) != NULL
) {
383 if ((enable
&& prproxy
) || (!enable
&& !prproxy
)) {
392 if (set_allmulti
&& ifp
!= NULL
) {
393 if_allmulti(ifp
, allmulti_sw
);
399 rt_set_proxy(rt
, enable
);
402 nd6_ndprl_free(down
);
407 * Enable/disable prefix proxying on an interface; typically called
408 * as part of handling SIOCSIFINFO_FLAGS[IFEF_IPV6_ROUTER].
411 nd6_if_prproxy(struct ifnet
*ifp
, boolean_t enable
)
413 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
414 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
415 struct nd6_prproxy_prelist
*up
, *down
;
416 struct nd_prefix
*pr
;
418 /* Can't be enabled if we are an advertising router on the interface */
419 ifnet_lock_shared(ifp
);
420 if (enable
&& (ifp
->if_eflags
& IFEF_IPV6_ROUTER
)) {
421 ifnet_lock_done(ifp
);
424 ifnet_lock_done(ifp
);
426 SLIST_INIT(&up_head
);
427 SLIST_INIT(&down_head
);
430 * Serialize the clearing/setting of NDPRF_PRPROXY.
432 lck_mtx_lock(&proxy6_lock
);
435 * First build a list of upstream prefixes on this interface for
436 * which we need to enable/disable prefix proxy functionality.
438 lck_mtx_lock(nd6_mutex
);
439 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
441 if (IN6_IS_ADDR_LINKLOCAL(&pr
->ndpr_prefix
.sin6_addr
) ||
442 (!enable
&& !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
443 (enable
&& (pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
444 (pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
445 pr
->ndpr_ifp
!= ifp
) {
451 * At present, in order for the prefix to be eligible
452 * as a proxying/proxied prefix, we require that the
453 * prefix route entry be marked as a cloning route with
454 * RTF_PROXY; i.e. nd6_need_cache() needs to return
455 * true for the interface type.
457 if (enable
&& (pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
458 nd6_need_cache(ifp
)) {
459 pr
->ndpr_stateflags
|= NDPRF_PRPROXY
;
460 NDPR_ADDREF_LOCKED(pr
);
462 } else if (!enable
) {
463 pr
->ndpr_stateflags
&= ~NDPRF_PRPROXY
;
464 NDPR_ADDREF_LOCKED(pr
);
468 pr
= NULL
; /* don't go further */
475 up
= nd6_ndprl_alloc(M_WAITOK
);
481 up
->ndprl_pr
= pr
; /* keep reference from above */
482 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
486 * Now build a list of matching (scoped) downstream prefixes on other
487 * interfaces which need to be enabled/disabled accordingly. Note that
488 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
490 SLIST_FOREACH(up
, &up_head
, ndprl_le
) {
491 struct nd_prefix
*fwd
;
492 struct in6_addr pr_addr
;
498 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
499 pr_len
= pr
->ndpr_plen
;
502 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
504 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
505 !(fwd
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
506 fwd
->ndpr_plen
!= pr_len
||
507 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
514 down
= nd6_ndprl_alloc(M_WAITOK
);
520 down
->ndprl_pr
= fwd
;
523 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
526 lck_mtx_unlock(nd6_mutex
);
529 * Apply routing function on prefixes; callee will free resources.
531 nd6_prproxy_prelist_setroute(enable
,
532 (struct nd6_prproxy_prelist_head
*)&up_head
,
533 (struct nd6_prproxy_prelist_head
*)&down_head
);
535 VERIFY(SLIST_EMPTY(&up_head
));
536 VERIFY(SLIST_EMPTY(&down_head
));
538 lck_mtx_unlock(&proxy6_lock
);
544 * Called from the input path to determine whether the packet is destined
545 * to a proxied node; if so, mark the mbuf with PKTFF_PROXY_DST so that
546 * icmp6_input() knows that this is not to be delivered to socket(s).
549 nd6_prproxy_isours(struct mbuf
*m
, struct ip6_hdr
*ip6
, struct route_in6
*ro6
,
550 unsigned int ifscope
)
553 boolean_t ours
= FALSE
;
555 if (ip6
->ip6_hlim
!= IPV6_MAXHLIM
|| ip6
->ip6_nxt
!= IPPROTO_ICMPV6
) {
559 if (IN6_IS_ADDR_MC_NODELOCAL(&ip6
->ip6_dst
) ||
560 IN6_IS_ADDR_MC_LINKLOCAL(&ip6
->ip6_dst
)) {
564 } else if (IN6_IS_ADDR_MULTICAST(&ip6
->ip6_dst
)) {
572 if ((rt
= ro6
->ro_rt
) != NULL
) {
576 if (ROUTE_UNUSABLE(ro6
)) {
583 /* Caller must have ensured this condition (not srcrt) */
584 VERIFY(IN6_ARE_ADDR_EQUAL(&ip6
->ip6_dst
,
585 &ro6
->ro_dst
.sin6_addr
));
587 rtalloc_scoped_ign((struct route
*)ro6
, RTF_PRCLONING
, ifscope
);
588 if ((rt
= ro6
->ro_rt
) == NULL
) {
595 ours
= (rt
->rt_flags
& RTF_PROXY
) ? TRUE
: FALSE
;
600 m
->m_pkthdr
.pkt_flags
|= PKTF_PROXY_DST
;
607 * Called from the input path to determine whether or not the proxy
608 * route entry is pointing to the correct interface, and to perform
609 * the necessary route fixups otherwise.
612 nd6_proxy_find_fwdroute(struct ifnet
*ifp
, struct route_in6
*ro6
)
614 struct in6_addr
*dst6
= &ro6
->ro_dst
.sin6_addr
;
615 struct ifnet
*fwd_ifp
= NULL
;
616 struct nd_prefix
*pr
;
619 if ((rt
= ro6
->ro_rt
) != NULL
) {
621 if (!(rt
->rt_flags
& RTF_PROXY
) || rt
->rt_ifp
== ifp
) {
622 nd6log2(debug
, "%s: found incorrect prefix "
623 "proxy route for dst %s on %s\n", if_name(ifp
),
625 if_name(rt
->rt_ifp
));
627 /* look it up below */
631 * The route is already marked with RTF_PRPROXY and
632 * it isn't pointing back to the inbound interface;
633 * optimistically return (see notes below).
640 * Find out where we should forward this packet to, by searching
641 * for another interface that is proxying for the prefix. Our
642 * current implementation assumes that the proxied prefix is shared
643 * to no more than one downstream interfaces (typically a bridge
646 lck_mtx_lock(nd6_mutex
);
647 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
648 struct in6_addr pr_addr
;
649 struct nd_prefix
*fwd
;
653 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
654 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
655 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
656 dst6
, &pr
->ndpr_mask
)) {
661 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
662 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
663 pr_len
= pr
->ndpr_plen
;
666 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
668 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
669 fwd
->ndpr_ifp
== ifp
||
670 fwd
->ndpr_plen
!= pr_len
||
671 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
677 fwd_ifp
= fwd
->ndpr_ifp
;
683 lck_mtx_unlock(nd6_mutex
);
685 lck_mtx_lock(rnh_lock
);
686 ROUTE_RELEASE_LOCKED(ro6
);
689 * Lookup a forwarding route; delete the route if it's incorrect,
690 * or return to caller if the correct one got created prior to
691 * our acquiring the rnh_lock.
693 if ((rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 0,
694 RTF_CLONING
| RTF_PRCLONING
, IFSCOPE_NONE
)) != NULL
) {
696 if (rt
->rt_ifp
!= fwd_ifp
|| !(rt
->rt_flags
& RTF_PROXY
)) {
697 rt
->rt_flags
|= RTF_CONDEMNED
;
699 (void) rtrequest_locked(RTM_DELETE
, rt_key(rt
),
700 rt
->rt_gateway
, rt_mask(rt
), rt
->rt_flags
, NULL
);
704 nd6log2(debug
, "%s: found prefix proxy route "
705 "for dst %s\n", if_name(rt
->rt_ifp
),
708 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
709 lck_mtx_unlock(rnh_lock
);
713 VERIFY(rt
== NULL
&& ro6
->ro_rt
== NULL
);
716 * Clone a route from the correct parent prefix route and return it.
718 if (fwd_ifp
!= NULL
&& (rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 1,
719 RTF_PRCLONING
, fwd_ifp
->if_index
)) != NULL
) {
721 if (!(rt
->rt_flags
& RTF_PROXY
)) {
726 nd6log2(debug
, "%s: allocated prefix proxy "
727 "route for dst %s\n", if_name(rt
->rt_ifp
),
730 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
733 VERIFY(rt
!= NULL
|| ro6
->ro_rt
== NULL
);
735 if (fwd_ifp
== NULL
|| rt
== NULL
) {
736 nd6log2(error
, "%s: failed to find forwarding prefix "
737 "proxy entry for dst %s\n", if_name(ifp
),
740 lck_mtx_unlock(rnh_lock
);
744 * Called when a prefix transitions between on-link and off-link. Perform
745 * routing (RTF_PROXY) and interface (all-multicast) related operations on
746 * the affected prefixes.
749 nd6_prproxy_prelist_update(struct nd_prefix
*pr_cur
, struct nd_prefix
*pr_up
)
751 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
752 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
753 struct nd6_prproxy_prelist
*up
, *down
;
754 struct nd_prefix
*pr
;
755 struct in6_addr pr_addr
;
759 SLIST_INIT(&up_head
);
760 SLIST_INIT(&down_head
);
761 VERIFY(pr_cur
!= NULL
);
763 LCK_MTX_ASSERT(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
766 * Upstream prefix. If caller did not specify one, search for one
767 * based on the information in current prefix. Caller is expected
768 * to have held an extra reference for the passed-in prefixes.
770 lck_mtx_lock(nd6_mutex
);
773 bcopy(&pr_cur
->ndpr_prefix
.sin6_addr
, &pr_addr
,
775 pr_len
= pr_cur
->ndpr_plen
;
778 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
780 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
781 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
782 pr
->ndpr_plen
!= pr_len
||
783 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
792 if ((pr_up
= pr
) == NULL
) {
793 lck_mtx_unlock(nd6_mutex
);
799 bcopy(&pr_up
->ndpr_prefix
.sin6_addr
, &pr_addr
,
801 pr_len
= pr_up
->ndpr_plen
;
803 NDPR_LOCK_ASSERT_HELD(pr_up
);
805 * Upstream prefix could be offlink by now; therefore we cannot
806 * assert that NDPRF_PRPROXY is set; however, we can insist that
807 * it must not be a scoped prefix.
809 VERIFY(!(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
));
810 enable
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
813 up
= nd6_ndprl_alloc(M_WAITOK
);
815 lck_mtx_unlock(nd6_mutex
);
820 up
->ndprl_pr
= pr_up
;
821 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
824 * Now build a list of matching (scoped) downstream prefixes on other
825 * interfaces which need to be enabled/disabled accordingly. Note that
826 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
828 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
830 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
831 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
832 pr
->ndpr_plen
!= pr_len
||
833 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
840 down
= nd6_ndprl_alloc(M_WAITOK
);
848 down
->ndprl_up
= pr_up
;
849 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
851 lck_mtx_unlock(nd6_mutex
);
854 * Apply routing function on prefixes; callee will free resources.
856 nd6_prproxy_prelist_setroute(enable
,
857 (struct nd6_prproxy_prelist_head
*)&up_head
,
858 (struct nd6_prproxy_prelist_head
*)&down_head
);
861 VERIFY(SLIST_EMPTY(&up_head
));
862 VERIFY(SLIST_EMPTY(&down_head
));
866 * Given an interface address, determine whether or not the address
867 * is part of of a proxied prefix.
870 nd6_prproxy_ifaddr(struct in6_ifaddr
*ia
)
872 struct nd_prefix
*pr
;
873 struct in6_addr addr
, pr_mask
;
875 boolean_t proxied
= FALSE
;
877 LCK_MTX_ASSERT(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
879 IFA_LOCK(&ia
->ia_ifa
);
880 bcopy(&ia
->ia_addr
.sin6_addr
, &addr
, sizeof(addr
));
881 bcopy(&ia
->ia_prefixmask
.sin6_addr
, &pr_mask
, sizeof(pr_mask
));
882 pr_len
= ia
->ia_plen
;
883 IFA_UNLOCK(&ia
->ia_ifa
);
885 lck_mtx_lock(nd6_mutex
);
886 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
888 if ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
889 (pr
->ndpr_stateflags
& NDPRF_PRPROXY
) &&
890 in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
898 lck_mtx_unlock(nd6_mutex
);
904 * Perform automatic proxy function with NS output.
906 * If the target address matches a global prefix obtained from a router
907 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
908 * flag set, then we send solicitations for the target address to all other
909 * interfaces where a matching prefix is currently on-link, in addition to
910 * the original interface.
913 nd6_prproxy_ns_output(struct ifnet
*ifp
, struct ifnet
*exclifp
,
914 struct in6_addr
*daddr
, struct in6_addr
*taddr
, struct llinfo_nd6
*ln
)
916 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
917 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
918 struct nd_prefix
*pr
, *fwd
;
919 struct ifnet
*fwd_ifp
;
920 struct in6_addr pr_addr
;
924 * Ignore excluded interface if it's the same as the original;
925 * we always send a NS on the original interface down below.
927 if (exclifp
!= NULL
&& exclifp
== ifp
) {
931 if (exclifp
== NULL
) {
932 nd6log2(debug
, "%s: sending NS who has %s on ALL\n",
933 if_name(ifp
), ip6_sprintf(taddr
));
935 nd6log2(debug
, "%s: sending NS who has %s on ALL "
936 "(except %s)\n", if_name(ifp
),
937 ip6_sprintf(taddr
), if_name(exclifp
));
940 SLIST_INIT(&ndprl_head
);
942 lck_mtx_lock(nd6_mutex
);
944 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
946 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
947 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
948 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
949 taddr
, &pr
->ndpr_mask
)) {
954 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
955 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
956 pr_len
= pr
->ndpr_plen
;
959 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
961 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
962 fwd
->ndpr_ifp
== ifp
|| fwd
->ndpr_ifp
== exclifp
||
963 fwd
->ndpr_plen
!= pr_len
||
964 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
970 fwd_ifp
= fwd
->ndpr_ifp
;
973 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
979 ndprl
->ndprl_pr
= fwd
;
980 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
982 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
987 lck_mtx_unlock(nd6_mutex
);
989 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
990 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
992 pr
= ndprl
->ndprl_pr
;
993 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
995 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
997 nd6_ndprl_free(ndprl
);
1002 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
1005 "%s: Sending cloned NS who has %s, originally "
1006 "on %s\n", if_name(fwd_ifp
),
1007 ip6_sprintf(taddr
), if_name(ifp
));
1009 nd6_ns_output(fwd_ifp
, daddr
, taddr
, NULL
, NULL
);
1015 nd6_ndprl_free(ndprl
);
1017 VERIFY(SLIST_EMPTY(&ndprl_head
));
1019 nd6_ns_output(ifp
, daddr
, taddr
, ln
, NULL
);
1023 * Perform automatic proxy function with NS input.
1025 * If the target address matches a global prefix obtained from a router
1026 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
1027 * flag set, then we send solicitations for the target address to all other
1028 * interfaces where a matching prefix is currently on-link.
1031 nd6_prproxy_ns_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
1032 char *lladdr
, int lladdrlen
, struct in6_addr
*daddr
,
1033 struct in6_addr
*taddr
, uint8_t *nonce
)
1035 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
1036 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
1037 struct nd_prefix
*pr
, *fwd
;
1038 struct ifnet
*fwd_ifp
;
1039 struct in6_addr pr_addr
;
1041 boolean_t solrec
= FALSE
;
1043 SLIST_INIT(&ndprl_head
);
1045 lck_mtx_lock(nd6_mutex
);
1047 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1049 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1050 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1051 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1052 taddr
, &pr
->ndpr_mask
)) {
1057 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1058 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
1059 pr_len
= pr
->ndpr_plen
;
1062 * If this is a NS for NUD/AR, record it so that we know
1063 * how to forward the NA reply later on (if/when it arrives.)
1064 * Give up if we fail to save the NS info.
1066 if ((solrec
= !IN6_IS_ADDR_UNSPECIFIED(saddr
)) &&
1067 !nd6_solsrc_enq(pr
, ifp
, saddr
, taddr
)) {
1070 break; /* bail out */
1075 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
1077 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1078 fwd
->ndpr_ifp
== ifp
||
1079 fwd
->ndpr_plen
!= pr_len
||
1080 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
1081 &pr_addr
, pr_len
)) {
1086 fwd_ifp
= fwd
->ndpr_ifp
;
1089 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1090 if (ndprl
== NULL
) {
1095 ndprl
->ndprl_pr
= fwd
;
1096 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1097 ndprl
->ndprl_sol
= solrec
;
1099 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1104 lck_mtx_unlock(nd6_mutex
);
1107 * If this is a recorded solicitation (NS for NUD/AR), create
1108 * or update the neighbor cache entry for the soliciting node.
1109 * Later on, when the NA reply arrives, we will need this cache
1110 * entry in order to send the NA back to the original solicitor.
1111 * Without a neighbor cache entry, we'd end up with an endless
1112 * cycle of NS ping-pong between the us (the proxy) and the node
1113 * which is soliciting for the address.
1116 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1117 nd6_cache_lladdr(ifp
, saddr
, lladdr
, lladdrlen
,
1118 ND_NEIGHBOR_SOLICIT
, 0);
1121 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1122 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1124 pr
= ndprl
->ndprl_pr
;
1125 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1127 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
1129 nd6_ndprl_free(ndprl
);
1134 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
1137 "%s: Forwarding NS (%s) from %s to %s who "
1138 "has %s, originally on %s\n", if_name(fwd_ifp
),
1139 ndprl
->ndprl_sol
? "NUD/AR" :
1140 "DAD", ip6_sprintf(saddr
), ip6_sprintf(daddr
),
1141 ip6_sprintf(taddr
), if_name(ifp
));
1143 nd6_ns_output(fwd_ifp
, ndprl
->ndprl_sol
? taddr
: NULL
,
1144 taddr
, NULL
, nonce
);
1150 nd6_ndprl_free(ndprl
);
1152 VERIFY(SLIST_EMPTY(&ndprl_head
));
1156 * Perform automatic proxy function with NA input.
1158 * If the target address matches a global prefix obtained from a router
1159 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES flag
1160 * set, then we send neighbor advertisements for the target address on all
1161 * other interfaces where a matching prefix is currently on link.
1164 nd6_prproxy_na_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
1165 struct in6_addr
*daddr0
, struct in6_addr
*taddr
, int flags
)
1167 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
1168 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
1169 struct nd_prefix
*pr
;
1170 struct ifnet
*fwd_ifp
;
1171 struct in6_addr daddr
;
1173 SLIST_INIT(&ndprl_head
);
1176 lck_mtx_lock(nd6_mutex
);
1178 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1180 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1181 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1182 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1183 taddr
, &pr
->ndpr_mask
)) {
1188 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1190 * If this is a NA for NUD, see if there is a record created
1191 * for the corresponding NS; upon success, we get back the
1192 * interface where the NS originally arrived on, as well as
1193 * the soliciting node's address. Give up if we can't find it.
1195 if (!IN6_IS_ADDR_MULTICAST(daddr0
)) {
1197 bzero(&daddr
, sizeof(daddr
));
1198 if (!nd6_solsrc_deq(pr
, taddr
, &daddr
, &fwd_ifp
)) {
1200 break; /* bail out */
1202 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
) && fwd_ifp
);
1205 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1206 if (ndprl
== NULL
) {
1207 break; /* bail out */
1209 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1210 ndprl
->ndprl_sol
= TRUE
;
1211 ndprl
->ndprl_sol_saddr
= *(&daddr
);
1213 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1215 struct nd_prefix
*fwd
;
1216 struct in6_addr pr_addr
;
1219 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
,
1221 pr_len
= pr
->ndpr_plen
;
1224 for (fwd
= nd_prefix
.lh_first
; fwd
;
1225 fwd
= fwd
->ndpr_next
) {
1227 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1228 fwd
->ndpr_ifp
== ifp
||
1229 fwd
->ndpr_plen
!= pr_len
||
1230 !in6_are_prefix_equal(
1231 &fwd
->ndpr_prefix
.sin6_addr
,
1232 &pr_addr
, pr_len
)) {
1237 fwd_ifp
= fwd
->ndpr_ifp
;
1240 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1241 if (ndprl
== NULL
) {
1246 ndprl
->ndprl_pr
= fwd
;
1247 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1249 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1255 lck_mtx_unlock(nd6_mutex
);
1257 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1260 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1262 pr
= ndprl
->ndprl_pr
;
1263 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1265 if (ndprl
->ndprl_sol
) {
1267 daddr
= *(&ndprl
->ndprl_sol_saddr
);
1268 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
));
1269 send_na
= (in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1274 send_na
= ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
1275 in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1280 if (!ndprl
->ndprl_sol
) {
1282 "%s: Forwarding NA (DAD) from %s to %s "
1283 "tgt is %s, originally on %s\n",
1285 ip6_sprintf(saddr
), ip6_sprintf(&daddr
),
1286 ip6_sprintf(taddr
), if_name(ifp
));
1289 "%s: Forwarding NA (NUD/AR) from %s to "
1290 "%s (was %s) tgt is %s, originally on "
1291 "%s\n", if_name(fwd_ifp
),
1293 ip6_sprintf(&daddr
), ip6_sprintf(daddr0
),
1294 ip6_sprintf(taddr
), if_name(ifp
));
1297 nd6_na_output(fwd_ifp
, &daddr
, taddr
, flags
, 1, NULL
);
1304 nd6_ndprl_free(ndprl
);
1306 VERIFY(SLIST_EMPTY(&ndprl_head
));
1309 static struct nd6_prproxy_solsrc
*
1310 nd6_solsrc_alloc(int how
)
1312 struct nd6_prproxy_solsrc
*ssrc
;
1314 ssrc
= (how
== M_WAITOK
) ? zalloc(solsrc_zone
) :
1315 zalloc_noblock(solsrc_zone
);
1317 bzero(ssrc
, solsrc_size
);
1324 nd6_solsrc_free(struct nd6_prproxy_solsrc
*ssrc
)
1326 zfree(solsrc_zone
, ssrc
);
1330 nd6_prproxy_sols_purge(struct nd_prefix
*pr
, u_int64_t max_stgt
)
1332 struct nd6_prproxy_soltgt
*soltgt
, *tmp
;
1333 u_int64_t expire
= (max_stgt
> 0) ? net_uptime() : 0;
1335 NDPR_LOCK_ASSERT_HELD(pr
);
1337 /* Either trim all or those that have expired or are idle */
1338 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1339 &pr
->ndpr_prproxy_sols
, tmp
) {
1340 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1341 if (expire
== 0 || soltgt
->soltgt_expire
<= expire
||
1342 soltgt
->soltgt_cnt
== 0) {
1343 pr
->ndpr_prproxy_sols_cnt
--;
1344 RB_REMOVE(prproxy_sols_tree
,
1345 &pr
->ndpr_prproxy_sols
, soltgt
);
1346 nd6_soltgt_free(soltgt
);
1350 if (max_stgt
== 0 || pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1351 VERIFY(max_stgt
!= 0 || (pr
->ndpr_prproxy_sols_cnt
== 0 &&
1352 RB_EMPTY(&pr
->ndpr_prproxy_sols
)));
1356 /* Brute force; mercilessly evict entries until we are under limit */
1357 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1358 &pr
->ndpr_prproxy_sols
, tmp
) {
1359 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1360 pr
->ndpr_prproxy_sols_cnt
--;
1361 RB_REMOVE(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1362 nd6_soltgt_free(soltgt
);
1363 if (pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1370 * Purges all solicitation records on a given prefix.
1371 * Caller is responsible for holding prefix lock.
1374 nd6_prproxy_sols_reap(struct nd_prefix
*pr
)
1376 nd6_prproxy_sols_purge(pr
, 0);
1380 * Purges expired or idle solicitation records on a given prefix.
1381 * Caller is responsible for holding prefix lock.
1384 nd6_prproxy_sols_prune(struct nd_prefix
*pr
, u_int32_t max_stgt
)
1386 nd6_prproxy_sols_purge(pr
, max_stgt
);
1390 * Enqueue a soliciation record in the target record of a prefix.
1393 nd6_solsrc_enq(struct nd_prefix
*pr
, struct ifnet
*ifp
,
1394 struct in6_addr
*saddr
, struct in6_addr
*taddr
)
1396 struct nd6_prproxy_soltgt find
, *soltgt
;
1397 struct nd6_prproxy_solsrc
*ssrc
;
1398 u_int32_t max_stgt
= nd6_max_tgt_sols
;
1399 u_int32_t max_ssrc
= nd6_max_src_sols
;
1401 NDPR_LOCK_ASSERT_HELD(pr
);
1402 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1403 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
| NDPRF_PRPROXY
)) ==
1404 (NDPRF_ONLINK
| NDPRF_PRPROXY
));
1405 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1407 ssrc
= nd6_solsrc_alloc(M_WAITOK
);
1412 ssrc
->solsrc_saddr
= *saddr
;
1413 ssrc
->solsrc_ifp
= ifp
;
1415 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1417 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1418 if (soltgt
== NULL
) {
1419 if (max_stgt
!= 0 && pr
->ndpr_prproxy_sols_cnt
>= max_stgt
) {
1420 VERIFY(!RB_EMPTY(&pr
->ndpr_prproxy_sols
));
1421 nd6_prproxy_sols_prune(pr
, max_stgt
);
1422 VERIFY(pr
->ndpr_prproxy_sols_cnt
< max_stgt
);
1425 soltgt
= nd6_soltgt_alloc(M_WAITOK
);
1426 if (soltgt
== NULL
) {
1427 nd6_solsrc_free(ssrc
);
1431 soltgt
->soltgt_key
.taddr
= *taddr
;
1432 VERIFY(soltgt
->soltgt_cnt
== 0);
1433 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1435 pr
->ndpr_prproxy_sols_cnt
++;
1436 VERIFY(pr
->ndpr_prproxy_sols_cnt
!= 0);
1437 RB_INSERT(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1440 if (max_ssrc
!= 0 && soltgt
->soltgt_cnt
>= max_ssrc
) {
1441 VERIFY(!TAILQ_EMPTY(&soltgt
->soltgt_q
));
1442 nd6_soltgt_prune(soltgt
, max_ssrc
);
1443 VERIFY(soltgt
->soltgt_cnt
< max_ssrc
);
1446 soltgt
->soltgt_cnt
++;
1447 VERIFY(soltgt
->soltgt_cnt
!= 0);
1448 TAILQ_INSERT_TAIL(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1449 if (soltgt
->soltgt_cnt
== 1) {
1450 soltgt
->soltgt_expire
= net_uptime() + ND6_TGT_SOLS_EXPIRE
;
1457 * Dequeue a solicitation record from a target record of a prefix.
1460 nd6_solsrc_deq(struct nd_prefix
*pr
, struct in6_addr
*taddr
,
1461 struct in6_addr
*daddr
, struct ifnet
**ifp
)
1463 struct nd6_prproxy_soltgt find
, *soltgt
;
1464 struct nd6_prproxy_solsrc
*ssrc
;
1466 NDPR_LOCK_ASSERT_HELD(pr
);
1467 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1468 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
| NDPRF_PRPROXY
)) ==
1469 (NDPRF_ONLINK
| NDPRF_PRPROXY
));
1471 bzero(daddr
, sizeof(*daddr
));
1474 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1476 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1477 if (soltgt
== NULL
|| soltgt
->soltgt_cnt
== 0) {
1478 VERIFY(soltgt
== NULL
|| TAILQ_EMPTY(&soltgt
->soltgt_q
));
1482 VERIFY(soltgt
->soltgt_cnt
!= 0);
1483 --soltgt
->soltgt_cnt
;
1484 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1485 VERIFY(ssrc
!= NULL
);
1486 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1487 *daddr
= *(&ssrc
->solsrc_saddr
);
1488 *ifp
= ssrc
->solsrc_ifp
;
1489 nd6_solsrc_free(ssrc
);
1494 static struct nd6_prproxy_soltgt
*
1495 nd6_soltgt_alloc(int how
)
1497 struct nd6_prproxy_soltgt
*soltgt
;
1499 soltgt
= (how
== M_WAITOK
) ? zalloc(soltgt_zone
) :
1500 zalloc_noblock(soltgt_zone
);
1501 if (soltgt
!= NULL
) {
1502 bzero(soltgt
, soltgt_size
);
1503 TAILQ_INIT(&soltgt
->soltgt_q
);
1509 nd6_soltgt_free(struct nd6_prproxy_soltgt
*soltgt
)
1511 struct nd6_prproxy_solsrc
*ssrc
, *tssrc
;
1513 TAILQ_FOREACH_SAFE(ssrc
, &soltgt
->soltgt_q
, solsrc_tqe
, tssrc
) {
1514 VERIFY(soltgt
->soltgt_cnt
> 0);
1515 soltgt
->soltgt_cnt
--;
1516 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1517 nd6_solsrc_free(ssrc
);
1520 VERIFY(soltgt
->soltgt_cnt
== 0);
1521 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1523 zfree(soltgt_zone
, soltgt
);
1527 nd6_soltgt_prune(struct nd6_prproxy_soltgt
*soltgt
, u_int32_t max_ssrc
)
1529 while (soltgt
->soltgt_cnt
>= max_ssrc
) {
1530 struct nd6_prproxy_solsrc
*ssrc
;
1532 VERIFY(soltgt
->soltgt_cnt
!= 0);
1533 --soltgt
->soltgt_cnt
;
1534 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1535 VERIFY(ssrc
!= NULL
);
1536 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1537 nd6_solsrc_free(ssrc
);
1542 * Solicited target tree comparison function.
1544 * An ordered predicate is necessary; bcmp() is not documented to return
1545 * an indication of order, memcmp() is, and is an ISO C99 requirement.
1548 soltgt_cmp(const struct nd6_prproxy_soltgt
*a
,
1549 const struct nd6_prproxy_soltgt
*b
)
1551 return memcmp(&a
->soltgt_key
, &b
->soltgt_key
, sizeof(a
->soltgt_key
));