2 * Copyright (c) 2011-2013 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__
);
233 zone_change(ndprl_zone
, Z_EXPAND
, TRUE
);
234 zone_change(ndprl_zone
, Z_CALLERACCT
, FALSE
);
236 solsrc_size
= sizeof (struct nd6_prproxy_solsrc
);
237 solsrc_zone
= zinit(solsrc_size
, SOLSRC_ZONE_MAX
* solsrc_size
, 0,
239 if (solsrc_zone
== NULL
)
240 panic("%s: failed allocating solsrc_zone", __func__
);
242 zone_change(solsrc_zone
, Z_EXPAND
, TRUE
);
243 zone_change(solsrc_zone
, Z_CALLERACCT
, FALSE
);
245 soltgt_size
= sizeof (struct nd6_prproxy_soltgt
);
246 soltgt_zone
= zinit(soltgt_size
, SOLTGT_ZONE_MAX
* soltgt_size
, 0,
248 if (soltgt_zone
== NULL
)
249 panic("%s: failed allocating soltgt_zone", __func__
);
251 zone_change(soltgt_zone
, Z_EXPAND
, TRUE
);
252 zone_change(soltgt_zone
, Z_CALLERACCT
, FALSE
);
255 static struct nd6_prproxy_prelist
*
256 nd6_ndprl_alloc(int how
)
258 struct nd6_prproxy_prelist
*ndprl
;
260 ndprl
= (how
== M_WAITOK
) ? zalloc(ndprl_zone
) :
261 zalloc_noblock(ndprl_zone
);
263 bzero(ndprl
, ndprl_size
);
269 nd6_ndprl_free(struct nd6_prproxy_prelist
*ndprl
)
271 zfree(ndprl_zone
, ndprl
);
275 * Apply routing function on the affected upstream and downstream prefixes,
276 * i.e. either set or clear RTF_PROXY on the cloning prefix route; all route
277 * entries that were cloned off these prefixes will be blown away. Caller
278 * must have acquried proxy6_lock and must not be holding nd6_mutex.
281 nd6_prproxy_prelist_setroute(boolean_t enable
,
282 struct nd6_prproxy_prelist_head
*up_head
,
283 struct nd6_prproxy_prelist_head
*down_head
)
285 struct nd6_prproxy_prelist
*up
, *down
, *ndprl_tmp
;
286 struct nd_prefix
*pr
;
288 lck_mtx_assert(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
289 lck_mtx_assert(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
291 SLIST_FOREACH_SAFE(up
, up_head
, ndprl_le
, ndprl_tmp
) {
293 boolean_t prproxy
, set_allmulti
= FALSE
;
295 struct ifnet
*ifp
= NULL
;
297 SLIST_REMOVE(up_head
, up
, nd6_prproxy_prelist
, ndprl_le
);
299 VERIFY(up
->ndprl_up
== NULL
);
303 prproxy
= (pr
->ndpr_stateflags
& NDPRF_PRPROXY
);
304 VERIFY(!prproxy
|| ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
305 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
307 nd6_prproxy_sols_reap(pr
);
308 VERIFY(pr
->ndpr_prproxy_sols_cnt
== 0);
309 VERIFY(RB_EMPTY(&pr
->ndpr_prproxy_sols
));
311 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
313 pr
->ndpr_allmulti_cnt
++;
316 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
318 pr
->ndpr_allmulti_cnt
--;
323 if ((rt
= pr
->ndpr_rt
) != NULL
) {
324 if ((enable
&& prproxy
) || (!enable
&& !prproxy
))
333 /* Call the following ioctl after releasing NDPR lock */
334 if (set_allmulti
&& ifp
!= NULL
)
335 if_allmulti(ifp
, allmulti_sw
);
340 rt_set_proxy(rt
, enable
);
346 SLIST_FOREACH_SAFE(down
, down_head
, ndprl_le
, ndprl_tmp
) {
347 struct nd_prefix
*pr_up
;
349 boolean_t prproxy
, set_allmulti
= FALSE
;
351 struct ifnet
*ifp
= NULL
;
353 SLIST_REMOVE(down_head
, down
, nd6_prproxy_prelist
, ndprl_le
);
355 pr_up
= down
->ndprl_up
;
356 VERIFY(pr_up
!= NULL
);
360 prproxy
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
361 VERIFY(!prproxy
|| ((pr_up
->ndpr_stateflags
& NDPRF_ONLINK
) &&
362 !(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
366 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
367 pr
->ndpr_allmulti_cnt
++;
370 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
371 pr
->ndpr_allmulti_cnt
--;
376 if ((rt
= pr
->ndpr_rt
) != NULL
) {
377 if ((enable
&& prproxy
) || (!enable
&& !prproxy
))
385 if (set_allmulti
&& ifp
!= NULL
)
386 if_allmulti(ifp
, allmulti_sw
);
391 rt_set_proxy(rt
, enable
);
394 nd6_ndprl_free(down
);
399 * Enable/disable prefix proxying on an interface; typically called
400 * as part of handling SIOCSIFINFO_FLAGS[IFEF_IPV6_ROUTER].
403 nd6_if_prproxy(struct ifnet
*ifp
, boolean_t enable
)
405 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
406 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
407 struct nd6_prproxy_prelist
*up
, *down
;
408 struct nd_prefix
*pr
;
410 /* Can't be enabled if we are an advertising router on the interface */
411 ifnet_lock_shared(ifp
);
412 if (enable
&& (ifp
->if_eflags
& IFEF_IPV6_ROUTER
)) {
413 ifnet_lock_done(ifp
);
416 ifnet_lock_done(ifp
);
418 SLIST_INIT(&up_head
);
419 SLIST_INIT(&down_head
);
422 * Serialize the clearing/setting of NDPRF_PRPROXY.
424 lck_mtx_lock(&proxy6_lock
);
427 * First build a list of upstream prefixes on this interface for
428 * which we need to enable/disable prefix proxy functionality.
430 lck_mtx_lock(nd6_mutex
);
431 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
433 if (IN6_IS_ADDR_LINKLOCAL(&pr
->ndpr_prefix
.sin6_addr
) ||
434 (!enable
&& !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
435 (enable
&& (pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
436 (pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
437 pr
->ndpr_ifp
!= ifp
) {
443 * At present, in order for the prefix to be eligible
444 * as a proxying/proxied prefix, we require that the
445 * prefix route entry be marked as a cloning route with
446 * RTF_PROXY; i.e. nd6_need_cache() needs to return
447 * true for the interface type.
449 if (enable
&& (pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
450 nd6_need_cache(ifp
)) {
451 pr
->ndpr_stateflags
|= NDPRF_PRPROXY
;
452 NDPR_ADDREF_LOCKED(pr
);
454 } else if (!enable
) {
455 pr
->ndpr_stateflags
&= ~NDPRF_PRPROXY
;
456 NDPR_ADDREF_LOCKED(pr
);
460 pr
= NULL
; /* don't go further */
466 up
= nd6_ndprl_alloc(M_WAITOK
);
472 up
->ndprl_pr
= pr
; /* keep reference from above */
473 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
477 * Now build a list of matching (scoped) downstream prefixes on other
478 * interfaces which need to be enabled/disabled accordingly. Note that
479 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
481 SLIST_FOREACH(up
, &up_head
, ndprl_le
) {
482 struct nd_prefix
*fwd
;
483 struct in6_addr pr_addr
;
489 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
490 pr_len
= pr
->ndpr_plen
;
493 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
495 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
496 !(fwd
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
497 fwd
->ndpr_plen
!= pr_len
||
498 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
505 down
= nd6_ndprl_alloc(M_WAITOK
);
510 down
->ndprl_pr
= fwd
;
513 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
516 lck_mtx_unlock(nd6_mutex
);
519 * Apply routing function on prefixes; callee will free resources.
521 nd6_prproxy_prelist_setroute(enable
,
522 (struct nd6_prproxy_prelist_head
*)&up_head
,
523 (struct nd6_prproxy_prelist_head
*)&down_head
);
525 VERIFY(SLIST_EMPTY(&up_head
));
526 VERIFY(SLIST_EMPTY(&down_head
));
528 lck_mtx_unlock(&proxy6_lock
);
534 * Called from the input path to determine whether the packet is destined
535 * to a proxied node; if so, mark the mbuf with PKTFF_PROXY_DST so that
536 * icmp6_input() knows that this is not to be delivered to socket(s).
539 nd6_prproxy_isours(struct mbuf
*m
, struct ip6_hdr
*ip6
, struct route_in6
*ro6
,
540 unsigned int ifscope
)
543 boolean_t ours
= FALSE
;
545 if (ip6
->ip6_hlim
!= IPV6_MAXHLIM
|| ip6
->ip6_nxt
!= IPPROTO_ICMPV6
)
548 if (IN6_IS_ADDR_MC_NODELOCAL(&ip6
->ip6_dst
) ||
549 IN6_IS_ADDR_MC_LINKLOCAL(&ip6
->ip6_dst
)) {
553 } else if (IN6_IS_ADDR_MULTICAST(&ip6
->ip6_dst
)) {
560 if ((rt
= ro6
->ro_rt
) != NULL
)
563 if (ROUTE_UNUSABLE(ro6
)) {
569 /* Caller must have ensured this condition (not srcrt) */
570 VERIFY(IN6_ARE_ADDR_EQUAL(&ip6
->ip6_dst
,
571 &ro6
->ro_dst
.sin6_addr
));
573 rtalloc_scoped_ign((struct route
*)ro6
, RTF_PRCLONING
, ifscope
);
574 if ((rt
= ro6
->ro_rt
) == NULL
)
580 ours
= (rt
->rt_flags
& RTF_PROXY
) ? TRUE
: FALSE
;
585 m
->m_pkthdr
.pkt_flags
|= PKTF_PROXY_DST
;
591 * Called from the input path to determine whether or not the proxy
592 * route entry is pointing to the correct interface, and to perform
593 * the necessary route fixups otherwise.
596 nd6_proxy_find_fwdroute(struct ifnet
*ifp
, struct route_in6
*ro6
)
598 struct in6_addr
*dst6
= &ro6
->ro_dst
.sin6_addr
;
599 struct ifnet
*fwd_ifp
= NULL
;
600 struct nd_prefix
*pr
;
603 if ((rt
= ro6
->ro_rt
) != NULL
) {
605 if (!(rt
->rt_flags
& RTF_PROXY
) || rt
->rt_ifp
== ifp
) {
606 nd6log2((LOG_DEBUG
, "%s: found incorrect prefix "
607 "proxy route for dst %s on %s\n", if_name(ifp
),
609 if_name(rt
->rt_ifp
)));
611 /* look it up below */
615 * The route is already marked with RTF_PRPROXY and
616 * it isn't pointing back to the inbound interface;
617 * optimistically return (see notes below).
624 * Find out where we should forward this packet to, by searching
625 * for another interface that is proxying for the prefix. Our
626 * current implementation assumes that the proxied prefix is shared
627 * to no more than one downstream interfaces (typically a bridge
630 lck_mtx_lock(nd6_mutex
);
631 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
632 struct in6_addr pr_addr
;
633 struct nd_prefix
*fwd
;
637 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
638 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
639 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
640 dst6
, &pr
->ndpr_mask
)) {
645 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
646 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
647 pr_len
= pr
->ndpr_plen
;
650 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
652 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
653 fwd
->ndpr_ifp
== ifp
||
654 fwd
->ndpr_plen
!= pr_len
||
655 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
661 fwd_ifp
= fwd
->ndpr_ifp
;
667 lck_mtx_unlock(nd6_mutex
);
669 lck_mtx_lock(rnh_lock
);
670 ROUTE_RELEASE_LOCKED(ro6
);
673 * Lookup a forwarding route; delete the route if it's incorrect,
674 * or return to caller if the correct one got created prior to
675 * our acquiring the rnh_lock.
677 if ((rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 0,
678 RTF_CLONING
| RTF_PRCLONING
, IFSCOPE_NONE
)) != NULL
) {
680 if (rt
->rt_ifp
!= fwd_ifp
|| !(rt
->rt_flags
& RTF_PROXY
)) {
681 rt
->rt_flags
|= RTF_CONDEMNED
;
683 (void) rtrequest_locked(RTM_DELETE
, rt_key(rt
),
684 rt
->rt_gateway
, rt_mask(rt
), rt
->rt_flags
, NULL
);
688 nd6log2((LOG_DEBUG
, "%s: found prefix proxy route "
689 "for dst %s\n", if_name(rt
->rt_ifp
),
692 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
693 lck_mtx_unlock(rnh_lock
);
697 VERIFY(rt
== NULL
&& ro6
->ro_rt
== NULL
);
700 * Clone a route from the correct parent prefix route and return it.
702 if (fwd_ifp
!= NULL
&& (rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 1,
703 RTF_PRCLONING
, fwd_ifp
->if_index
)) != NULL
) {
705 if (!(rt
->rt_flags
& RTF_PROXY
)) {
710 nd6log2((LOG_DEBUG
, "%s: allocated prefix proxy "
711 "route for dst %s\n", if_name(rt
->rt_ifp
),
714 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
717 VERIFY(rt
!= NULL
|| ro6
->ro_rt
== NULL
);
719 if (fwd_ifp
== NULL
|| rt
== NULL
) {
720 nd6log2((LOG_ERR
, "%s: failed to find forwarding prefix "
721 "proxy entry for dst %s\n", if_name(ifp
),
724 lck_mtx_unlock(rnh_lock
);
728 * Called when a prefix transitions between on-link and off-link. Perform
729 * routing (RTF_PROXY) and interface (all-multicast) related operations on
730 * the affected prefixes.
733 nd6_prproxy_prelist_update(struct nd_prefix
*pr_cur
, struct nd_prefix
*pr_up
)
735 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
736 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
737 struct nd6_prproxy_prelist
*up
, *down
;
738 struct nd_prefix
*pr
;
739 struct in6_addr pr_addr
;
743 SLIST_INIT(&up_head
);
744 SLIST_INIT(&down_head
);
745 VERIFY(pr_cur
!= NULL
);
747 lck_mtx_assert(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
750 * Upstream prefix. If caller did not specify one, search for one
751 * based on the information in current prefix. Caller is expected
752 * to have held an extra reference for the passed-in prefixes.
754 lck_mtx_lock(nd6_mutex
);
757 bcopy(&pr_cur
->ndpr_prefix
.sin6_addr
, &pr_addr
,
759 pr_len
= pr_cur
->ndpr_plen
;
762 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
764 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
765 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
766 pr
->ndpr_plen
!= pr_len
||
767 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
776 if ((pr_up
= pr
) == NULL
) {
777 lck_mtx_unlock(nd6_mutex
);
783 bcopy(&pr_up
->ndpr_prefix
.sin6_addr
, &pr_addr
,
785 pr_len
= pr_up
->ndpr_plen
;
787 NDPR_LOCK_ASSERT_HELD(pr_up
);
789 * Upstream prefix could be offlink by now; therefore we cannot
790 * assert that NDPRF_PRPROXY is set; however, we can insist that
791 * it must not be a scoped prefix.
793 VERIFY(!(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
));
794 enable
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
797 up
= nd6_ndprl_alloc(M_WAITOK
);
799 lck_mtx_unlock(nd6_mutex
);
804 up
->ndprl_pr
= pr_up
;
805 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
808 * Now build a list of matching (scoped) downstream prefixes on other
809 * interfaces which need to be enabled/disabled accordingly. Note that
810 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
812 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
814 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
815 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
816 pr
->ndpr_plen
!= pr_len
||
817 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
824 down
= nd6_ndprl_alloc(M_WAITOK
);
831 down
->ndprl_up
= pr_up
;
832 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
834 lck_mtx_unlock(nd6_mutex
);
837 * Apply routing function on prefixes; callee will free resources.
839 nd6_prproxy_prelist_setroute(enable
,
840 (struct nd6_prproxy_prelist_head
*)&up_head
,
841 (struct nd6_prproxy_prelist_head
*)&down_head
);
844 VERIFY(SLIST_EMPTY(&up_head
));
845 VERIFY(SLIST_EMPTY(&down_head
));
849 * Given an interface address, determine whether or not the address
850 * is part of of a proxied prefix.
853 nd6_prproxy_ifaddr(struct in6_ifaddr
*ia
)
855 struct nd_prefix
*pr
;
856 struct in6_addr addr
, pr_mask
;
858 boolean_t proxied
= FALSE
;
860 lck_mtx_assert(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
862 IFA_LOCK(&ia
->ia_ifa
);
863 bcopy(&ia
->ia_addr
.sin6_addr
, &addr
, sizeof (addr
));
864 bcopy(&ia
->ia_prefixmask
.sin6_addr
, &pr_mask
, sizeof (pr_mask
));
865 pr_len
= ia
->ia_plen
;
866 IFA_UNLOCK(&ia
->ia_ifa
);
868 lck_mtx_lock(nd6_mutex
);
869 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
871 if ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
872 (pr
->ndpr_stateflags
& NDPRF_PRPROXY
) &&
873 in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
881 lck_mtx_unlock(nd6_mutex
);
887 * Perform automatic proxy function with NS output.
889 * If the target address matches a global prefix obtained from a router
890 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
891 * flag set, then we send solicitations for the target address to all other
892 * interfaces where a matching prefix is currently on-link, in addition to
893 * the original interface.
896 nd6_prproxy_ns_output(struct ifnet
*ifp
, struct ifnet
*exclifp
,
897 struct in6_addr
*daddr
, struct in6_addr
*taddr
, struct llinfo_nd6
*ln
)
899 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
900 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
901 struct nd_prefix
*pr
, *fwd
;
902 struct ifnet
*fwd_ifp
;
903 struct in6_addr pr_addr
;
907 * Ignore excluded interface if it's the same as the original;
908 * we always send a NS on the original interface down below.
910 if (exclifp
!= NULL
&& exclifp
== ifp
)
914 nd6log2((LOG_DEBUG
, "%s: sending NS who has %s on ALL\n",
915 if_name(ifp
), ip6_sprintf(taddr
)));
917 nd6log2((LOG_DEBUG
, "%s: sending NS who has %s on ALL "
918 "(except %s)\n", if_name(ifp
),
919 ip6_sprintf(taddr
), if_name(exclifp
)));
921 SLIST_INIT(&ndprl_head
);
923 lck_mtx_lock(nd6_mutex
);
925 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
927 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
928 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
929 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
930 taddr
, &pr
->ndpr_mask
)) {
935 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
936 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
937 pr_len
= pr
->ndpr_plen
;
940 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
942 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
943 fwd
->ndpr_ifp
== ifp
|| fwd
->ndpr_ifp
== exclifp
||
944 fwd
->ndpr_plen
!= pr_len
||
945 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
951 fwd_ifp
= fwd
->ndpr_ifp
;
954 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
959 ndprl
->ndprl_pr
= fwd
;
960 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
962 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
967 lck_mtx_unlock(nd6_mutex
);
969 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
970 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
972 pr
= ndprl
->ndprl_pr
;
973 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
975 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
977 nd6_ndprl_free(ndprl
);
982 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
985 "%s: Sending cloned NS who has %s, originally "
986 "on %s\n", if_name(fwd_ifp
),
987 ip6_sprintf(taddr
), if_name(ifp
)));
989 nd6_ns_output(fwd_ifp
, daddr
, taddr
, NULL
, 0);
995 nd6_ndprl_free(ndprl
);
997 VERIFY(SLIST_EMPTY(&ndprl_head
));
999 nd6_ns_output(ifp
, daddr
, taddr
, ln
, 0);
1003 * Perform automatic proxy function with NS input.
1005 * If the target address matches a global prefix obtained from a router
1006 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
1007 * flag set, then we send solicitations for the target address to all other
1008 * interfaces where a matching prefix is currently on-link.
1011 nd6_prproxy_ns_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
1012 char *lladdr
, int lladdrlen
, struct in6_addr
*daddr
, struct in6_addr
*taddr
)
1014 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
1015 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
1016 struct nd_prefix
*pr
, *fwd
;
1017 struct ifnet
*fwd_ifp
;
1018 struct in6_addr pr_addr
;
1020 boolean_t solrec
= FALSE
;
1022 SLIST_INIT(&ndprl_head
);
1024 lck_mtx_lock(nd6_mutex
);
1026 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1028 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1029 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1030 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1031 taddr
, &pr
->ndpr_mask
)) {
1036 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1037 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
1038 pr_len
= pr
->ndpr_plen
;
1041 * If this is a NS for NUD/AR, record it so that we know
1042 * how to forward the NA reply later on (if/when it arrives.)
1043 * Give up if we fail to save the NS info.
1045 if ((solrec
= !IN6_IS_ADDR_UNSPECIFIED(saddr
)) &&
1046 !nd6_solsrc_enq(pr
, ifp
, saddr
, taddr
)) {
1049 break; /* bail out */
1054 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
1056 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1057 fwd
->ndpr_ifp
== ifp
||
1058 fwd
->ndpr_plen
!= pr_len
||
1059 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
1060 &pr_addr
, pr_len
)) {
1065 fwd_ifp
= fwd
->ndpr_ifp
;
1068 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1073 ndprl
->ndprl_pr
= fwd
;
1074 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1075 ndprl
->ndprl_sol
= solrec
;
1077 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1082 lck_mtx_unlock(nd6_mutex
);
1085 * If this is a recorded solicitation (NS for NUD/AR), create
1086 * or update the neighbor cache entry for the soliciting node.
1087 * Later on, when the NA reply arrives, we will need this cache
1088 * entry in order to send the NA back to the original solicitor.
1089 * Without a neighbor cache entry, we'd end up with an endless
1090 * cycle of NS ping-pong between the us (the proxy) and the node
1091 * which is soliciting for the address.
1094 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1095 nd6_cache_lladdr(ifp
, saddr
, lladdr
, lladdrlen
,
1096 ND_NEIGHBOR_SOLICIT
, 0);
1099 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1100 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1102 pr
= ndprl
->ndprl_pr
;
1103 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1105 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
1107 nd6_ndprl_free(ndprl
);
1112 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
1115 "%s: Forwarding NS (%s) from %s to %s who "
1116 "has %s, originally on %s\n", if_name(fwd_ifp
),
1117 ndprl
->ndprl_sol
? "NUD/AR" :
1118 "DAD", ip6_sprintf(saddr
), ip6_sprintf(daddr
),
1119 ip6_sprintf(taddr
), if_name(ifp
)));
1121 nd6_ns_output(fwd_ifp
, ndprl
->ndprl_sol
? taddr
: NULL
,
1122 taddr
, NULL
, !ndprl
->ndprl_sol
);
1128 nd6_ndprl_free(ndprl
);
1130 VERIFY(SLIST_EMPTY(&ndprl_head
));
1134 * Perform automatic proxy function with NA input.
1136 * If the target address matches a global prefix obtained from a router
1137 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES flag
1138 * set, then we send neighbor advertisements for the target address on all
1139 * other interfaces where a matching prefix is currently on link.
1142 nd6_prproxy_na_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
1143 struct in6_addr
*daddr0
, struct in6_addr
*taddr
, int flags
)
1145 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
1146 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
1147 struct nd_prefix
*pr
;
1148 struct ifnet
*fwd_ifp
;
1149 struct in6_addr daddr
;
1151 SLIST_INIT(&ndprl_head
);
1154 lck_mtx_lock(nd6_mutex
);
1156 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1158 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1159 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1160 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1161 taddr
, &pr
->ndpr_mask
)) {
1166 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1168 * If this is a NA for NUD, see if there is a record created
1169 * for the corresponding NS; upon success, we get back the
1170 * interface where the NS originally arrived on, as well as
1171 * the soliciting node's address. Give up if we can't find it.
1173 if (!IN6_IS_ADDR_MULTICAST(daddr0
)) {
1175 bzero(&daddr
, sizeof (daddr
));
1176 if (!nd6_solsrc_deq(pr
, taddr
, &daddr
, &fwd_ifp
)) {
1178 break; /* bail out */
1180 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
) && fwd_ifp
);
1183 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1185 break; /* bail out */
1187 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1188 ndprl
->ndprl_sol
= TRUE
;
1189 ndprl
->ndprl_sol_saddr
= *(&daddr
);
1191 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1193 struct nd_prefix
*fwd
;
1194 struct in6_addr pr_addr
;
1197 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
,
1199 pr_len
= pr
->ndpr_plen
;
1202 for (fwd
= nd_prefix
.lh_first
; fwd
;
1203 fwd
= fwd
->ndpr_next
) {
1205 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1206 fwd
->ndpr_ifp
== ifp
||
1207 fwd
->ndpr_plen
!= pr_len
||
1208 !in6_are_prefix_equal(
1209 &fwd
->ndpr_prefix
.sin6_addr
,
1210 &pr_addr
, pr_len
)) {
1215 fwd_ifp
= fwd
->ndpr_ifp
;
1218 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1223 ndprl
->ndprl_pr
= fwd
;
1224 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1226 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1232 lck_mtx_unlock(nd6_mutex
);
1234 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1237 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1239 pr
= ndprl
->ndprl_pr
;
1240 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1242 if (ndprl
->ndprl_sol
) {
1244 daddr
= *(&ndprl
->ndprl_sol_saddr
);
1245 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
));
1246 send_na
= (in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1251 send_na
= ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
1252 in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1257 if (!ndprl
->ndprl_sol
) {
1259 "%s: Forwarding NA (DAD) from %s to %s "
1260 "tgt is %s, originally on %s\n",
1262 ip6_sprintf(saddr
), ip6_sprintf(&daddr
),
1263 ip6_sprintf(taddr
), if_name(ifp
)));
1266 "%s: Forwarding NA (NUD/AR) from %s to "
1267 "%s (was %s) tgt is %s, originally on "
1268 "%s\n", if_name(fwd_ifp
),
1270 ip6_sprintf(&daddr
), ip6_sprintf(daddr0
),
1271 ip6_sprintf(taddr
), if_name(ifp
)));
1274 nd6_na_output(fwd_ifp
, &daddr
, taddr
, flags
, 1, NULL
);
1280 nd6_ndprl_free(ndprl
);
1282 VERIFY(SLIST_EMPTY(&ndprl_head
));
1285 static struct nd6_prproxy_solsrc
*
1286 nd6_solsrc_alloc(int how
)
1288 struct nd6_prproxy_solsrc
*ssrc
;
1290 ssrc
= (how
== M_WAITOK
) ? zalloc(solsrc_zone
) :
1291 zalloc_noblock(solsrc_zone
);
1293 bzero(ssrc
, solsrc_size
);
1299 nd6_solsrc_free(struct nd6_prproxy_solsrc
*ssrc
)
1301 zfree(solsrc_zone
, ssrc
);
1305 nd6_prproxy_sols_purge(struct nd_prefix
*pr
, u_int64_t max_stgt
)
1307 struct nd6_prproxy_soltgt
*soltgt
, *tmp
;
1308 u_int64_t expire
= (max_stgt
> 0) ? net_uptime() : 0;
1310 NDPR_LOCK_ASSERT_HELD(pr
);
1312 /* Either trim all or those that have expired or are idle */
1313 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1314 &pr
->ndpr_prproxy_sols
, tmp
) {
1315 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1316 if (expire
== 0 || soltgt
->soltgt_expire
<= expire
||
1317 soltgt
->soltgt_cnt
== 0) {
1318 pr
->ndpr_prproxy_sols_cnt
--;
1319 RB_REMOVE(prproxy_sols_tree
,
1320 &pr
->ndpr_prproxy_sols
, soltgt
);
1321 nd6_soltgt_free(soltgt
);
1325 if (max_stgt
== 0 || pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1326 VERIFY(max_stgt
!= 0 || (pr
->ndpr_prproxy_sols_cnt
== 0 &&
1327 RB_EMPTY(&pr
->ndpr_prproxy_sols
)));
1331 /* Brute force; mercilessly evict entries until we are under limit */
1332 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1333 &pr
->ndpr_prproxy_sols
, tmp
) {
1334 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1335 pr
->ndpr_prproxy_sols_cnt
--;
1336 RB_REMOVE(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1337 nd6_soltgt_free(soltgt
);
1338 if (pr
->ndpr_prproxy_sols_cnt
< max_stgt
)
1344 * Purges all solicitation records on a given prefix.
1345 * Caller is responsible for holding prefix lock.
1348 nd6_prproxy_sols_reap(struct nd_prefix
*pr
)
1350 nd6_prproxy_sols_purge(pr
, 0);
1354 * Purges expired or idle solicitation records on a given prefix.
1355 * Caller is responsible for holding prefix lock.
1358 nd6_prproxy_sols_prune(struct nd_prefix
*pr
, u_int32_t max_stgt
)
1360 nd6_prproxy_sols_purge(pr
, max_stgt
);
1364 * Enqueue a soliciation record in the target record of a prefix.
1367 nd6_solsrc_enq(struct nd_prefix
*pr
, struct ifnet
*ifp
,
1368 struct in6_addr
*saddr
, struct in6_addr
*taddr
)
1370 struct nd6_prproxy_soltgt find
, *soltgt
;
1371 struct nd6_prproxy_solsrc
*ssrc
;
1372 u_int32_t max_stgt
= nd6_max_tgt_sols
;
1373 u_int32_t max_ssrc
= nd6_max_src_sols
;
1375 NDPR_LOCK_ASSERT_HELD(pr
);
1376 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1377 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
|NDPRF_PRPROXY
)) ==
1378 (NDPRF_ONLINK
|NDPRF_PRPROXY
));
1379 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1381 ssrc
= nd6_solsrc_alloc(M_WAITOK
);
1385 ssrc
->solsrc_saddr
= *saddr
;
1386 ssrc
->solsrc_ifp
= ifp
;
1388 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1390 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1391 if (soltgt
== NULL
) {
1392 if (max_stgt
!= 0 && pr
->ndpr_prproxy_sols_cnt
>= max_stgt
) {
1393 VERIFY(!RB_EMPTY(&pr
->ndpr_prproxy_sols
));
1394 nd6_prproxy_sols_prune(pr
, max_stgt
);
1395 VERIFY(pr
->ndpr_prproxy_sols_cnt
< max_stgt
);
1398 soltgt
= nd6_soltgt_alloc(M_WAITOK
);
1399 if (soltgt
== NULL
) {
1400 nd6_solsrc_free(ssrc
);
1404 soltgt
->soltgt_key
.taddr
= *taddr
;
1405 VERIFY(soltgt
->soltgt_cnt
== 0);
1406 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1408 pr
->ndpr_prproxy_sols_cnt
++;
1409 VERIFY(pr
->ndpr_prproxy_sols_cnt
!= 0);
1410 RB_INSERT(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1413 if (max_ssrc
!= 0 && soltgt
->soltgt_cnt
>= max_ssrc
) {
1414 VERIFY(!TAILQ_EMPTY(&soltgt
->soltgt_q
));
1415 nd6_soltgt_prune(soltgt
, max_ssrc
);
1416 VERIFY(soltgt
->soltgt_cnt
< max_ssrc
);
1419 soltgt
->soltgt_cnt
++;
1420 VERIFY(soltgt
->soltgt_cnt
!= 0);
1421 TAILQ_INSERT_TAIL(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1422 if (soltgt
->soltgt_cnt
== 1)
1423 soltgt
->soltgt_expire
= net_uptime() + ND6_TGT_SOLS_EXPIRE
;
1429 * Dequeue a solicitation record from a target record of a prefix.
1432 nd6_solsrc_deq(struct nd_prefix
*pr
, struct in6_addr
*taddr
,
1433 struct in6_addr
*daddr
, struct ifnet
**ifp
)
1435 struct nd6_prproxy_soltgt find
, *soltgt
;
1436 struct nd6_prproxy_solsrc
*ssrc
;
1438 NDPR_LOCK_ASSERT_HELD(pr
);
1439 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1440 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
|NDPRF_PRPROXY
)) ==
1441 (NDPRF_ONLINK
|NDPRF_PRPROXY
));
1443 bzero(daddr
, sizeof (*daddr
));
1446 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1448 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1449 if (soltgt
== NULL
|| soltgt
->soltgt_cnt
== 0) {
1450 VERIFY(soltgt
== NULL
|| TAILQ_EMPTY(&soltgt
->soltgt_q
));
1454 VERIFY(soltgt
->soltgt_cnt
!= 0);
1455 --soltgt
->soltgt_cnt
;
1456 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1457 VERIFY(ssrc
!= NULL
);
1458 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1459 *daddr
= *(&ssrc
->solsrc_saddr
);
1460 *ifp
= ssrc
->solsrc_ifp
;
1461 nd6_solsrc_free(ssrc
);
1466 static struct nd6_prproxy_soltgt
*
1467 nd6_soltgt_alloc(int how
)
1469 struct nd6_prproxy_soltgt
*soltgt
;
1471 soltgt
= (how
== M_WAITOK
) ? zalloc(soltgt_zone
) :
1472 zalloc_noblock(soltgt_zone
);
1473 if (soltgt
!= NULL
) {
1474 bzero(soltgt
, soltgt_size
);
1475 TAILQ_INIT(&soltgt
->soltgt_q
);
1481 nd6_soltgt_free(struct nd6_prproxy_soltgt
*soltgt
)
1483 struct nd6_prproxy_solsrc
*ssrc
, *tssrc
;
1485 TAILQ_FOREACH_SAFE(ssrc
, &soltgt
->soltgt_q
, solsrc_tqe
, tssrc
) {
1486 VERIFY(soltgt
->soltgt_cnt
> 0);
1487 soltgt
->soltgt_cnt
--;
1488 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1489 nd6_solsrc_free(ssrc
);
1492 VERIFY(soltgt
->soltgt_cnt
== 0);
1493 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1495 zfree(soltgt_zone
, soltgt
);
1499 nd6_soltgt_prune(struct nd6_prproxy_soltgt
*soltgt
, u_int32_t max_ssrc
)
1501 while (soltgt
->soltgt_cnt
>= max_ssrc
) {
1502 struct nd6_prproxy_solsrc
*ssrc
;
1504 VERIFY(soltgt
->soltgt_cnt
!= 0);
1505 --soltgt
->soltgt_cnt
;
1506 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1507 VERIFY(ssrc
!= NULL
);
1508 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1509 nd6_solsrc_free(ssrc
);
1514 * Solicited target tree comparison function.
1516 * An ordered predicate is necessary; bcmp() is not documented to return
1517 * an indication of order, memcmp() is, and is an ISO C99 requirement.
1520 soltgt_cmp(const struct nd6_prproxy_soltgt
*a
,
1521 const struct nd6_prproxy_soltgt
*b
)
1523 return (memcmp(&a
->soltgt_key
, &b
->soltgt_key
, sizeof (a
->soltgt_key
)));