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
) {
295 SLIST_REMOVE(up_head
, up
, nd6_prproxy_prelist
, ndprl_le
);
297 VERIFY(up
->ndprl_up
== NULL
);
300 prproxy
= (pr
->ndpr_stateflags
& NDPRF_PRPROXY
);
301 VERIFY(!prproxy
|| ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
302 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
304 nd6_prproxy_sols_reap(pr
);
305 VERIFY(pr
->ndpr_prproxy_sols_cnt
== 0);
306 VERIFY(RB_EMPTY(&pr
->ndpr_prproxy_sols
));
308 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
310 pr
->ndpr_allmulti_cnt
++;
311 if_allmulti(pr
->ndpr_ifp
, TRUE
);
312 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
314 pr
->ndpr_allmulti_cnt
--;
315 if_allmulti(pr
->ndpr_ifp
, FALSE
);
318 if ((rt
= pr
->ndpr_rt
) != NULL
) {
319 if ((enable
&& prproxy
) || (!enable
&& !prproxy
))
329 rt_set_proxy(rt
, enable
);
335 SLIST_FOREACH_SAFE(down
, down_head
, ndprl_le
, ndprl_tmp
) {
336 struct nd_prefix
*pr_up
;
340 SLIST_REMOVE(down_head
, down
, nd6_prproxy_prelist
, ndprl_le
);
342 pr_up
= down
->ndprl_up
;
343 VERIFY(pr_up
!= NULL
);
346 prproxy
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
347 VERIFY(!prproxy
|| ((pr_up
->ndpr_stateflags
& NDPRF_ONLINK
) &&
348 !(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
352 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
353 pr
->ndpr_allmulti_cnt
++;
354 if_allmulti(pr
->ndpr_ifp
, TRUE
);
355 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
356 pr
->ndpr_allmulti_cnt
--;
357 if_allmulti(pr
->ndpr_ifp
, FALSE
);
360 if ((rt
= pr
->ndpr_rt
) != NULL
) {
361 if ((enable
&& prproxy
) || (!enable
&& !prproxy
))
372 rt_set_proxy(rt
, enable
);
375 nd6_ndprl_free(down
);
380 * Enable/disable prefix proxying on an interface; typically called
381 * as part of handling SIOCSIFINFO_FLAGS[IFEF_IPV6_ROUTER].
384 nd6_if_prproxy(struct ifnet
*ifp
, boolean_t enable
)
386 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
387 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
388 struct nd6_prproxy_prelist
*up
, *down
;
389 struct nd_prefix
*pr
;
391 /* Can't be enabled if we are an advertising router on the interface */
392 ifnet_lock_shared(ifp
);
393 if (enable
&& (ifp
->if_eflags
& IFEF_IPV6_ROUTER
)) {
394 ifnet_lock_done(ifp
);
397 ifnet_lock_done(ifp
);
399 SLIST_INIT(&up_head
);
400 SLIST_INIT(&down_head
);
403 * Serialize the clearing/setting of NDPRF_PRPROXY.
405 lck_mtx_lock(&proxy6_lock
);
408 * First build a list of upstream prefixes on this interface for
409 * which we need to enable/disable prefix proxy functionality.
411 lck_mtx_lock(nd6_mutex
);
412 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
414 if (IN6_IS_ADDR_LINKLOCAL(&pr
->ndpr_prefix
.sin6_addr
) ||
415 (!enable
&& !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
416 (enable
&& (pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
417 (pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
418 pr
->ndpr_ifp
!= ifp
) {
424 * At present, in order for the prefix to be eligible
425 * as a proxying/proxied prefix, we require that the
426 * prefix route entry be marked as a cloning route with
427 * RTF_PROXY; i.e. nd6_need_cache() needs to return
428 * true for the interface type.
430 if (enable
&& (pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
431 nd6_need_cache(ifp
)) {
432 pr
->ndpr_stateflags
|= NDPRF_PRPROXY
;
433 NDPR_ADDREF_LOCKED(pr
);
435 } else if (!enable
) {
436 pr
->ndpr_stateflags
&= ~NDPRF_PRPROXY
;
437 NDPR_ADDREF_LOCKED(pr
);
441 pr
= NULL
; /* don't go further */
447 up
= nd6_ndprl_alloc(M_WAITOK
);
453 up
->ndprl_pr
= pr
; /* keep reference from above */
454 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
458 * Now build a list of matching (scoped) downstream prefixes on other
459 * interfaces which need to be enabled/disabled accordingly. Note that
460 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
462 SLIST_FOREACH(up
, &up_head
, ndprl_le
) {
463 struct nd_prefix
*fwd
;
464 struct in6_addr pr_addr
;
470 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
471 pr_len
= pr
->ndpr_plen
;
474 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
476 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
477 !(fwd
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
478 fwd
->ndpr_plen
!= pr_len
||
479 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
486 down
= nd6_ndprl_alloc(M_WAITOK
);
491 down
->ndprl_pr
= fwd
;
494 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
497 lck_mtx_unlock(nd6_mutex
);
500 * Apply routing function on prefixes; callee will free resources.
502 nd6_prproxy_prelist_setroute(enable
,
503 (struct nd6_prproxy_prelist_head
*)&up_head
,
504 (struct nd6_prproxy_prelist_head
*)&down_head
);
506 VERIFY(SLIST_EMPTY(&up_head
));
507 VERIFY(SLIST_EMPTY(&down_head
));
509 lck_mtx_unlock(&proxy6_lock
);
515 * Called from the input path to determine whether the packet is destined
516 * to a proxied node; if so, mark the mbuf with PKTFF_PROXY_DST so that
517 * icmp6_input() knows that this is not to be delivered to socket(s).
520 nd6_prproxy_isours(struct mbuf
*m
, struct ip6_hdr
*ip6
, struct route_in6
*ro6
,
521 unsigned int ifscope
)
524 boolean_t ours
= FALSE
;
526 if (ip6
->ip6_hlim
!= IPV6_MAXHLIM
|| ip6
->ip6_nxt
!= IPPROTO_ICMPV6
)
529 if (IN6_IS_ADDR_MC_NODELOCAL(&ip6
->ip6_dst
) ||
530 IN6_IS_ADDR_MC_LINKLOCAL(&ip6
->ip6_dst
)) {
534 } else if (IN6_IS_ADDR_MULTICAST(&ip6
->ip6_dst
)) {
541 if ((rt
= ro6
->ro_rt
) != NULL
)
544 if (ROUTE_UNUSABLE(ro6
)) {
550 /* Caller must have ensured this condition (not srcrt) */
551 VERIFY(IN6_ARE_ADDR_EQUAL(&ip6
->ip6_dst
,
552 &ro6
->ro_dst
.sin6_addr
));
554 rtalloc_scoped_ign((struct route
*)ro6
, RTF_PRCLONING
, ifscope
);
555 if ((rt
= ro6
->ro_rt
) == NULL
)
561 ours
= (rt
->rt_flags
& RTF_PROXY
) ? TRUE
: FALSE
;
566 m
->m_pkthdr
.pkt_flags
|= PKTF_PROXY_DST
;
572 * Called from the input path to determine whether or not the proxy
573 * route entry is pointing to the correct interface, and to perform
574 * the necessary route fixups otherwise.
577 nd6_proxy_find_fwdroute(struct ifnet
*ifp
, struct route_in6
*ro6
)
579 struct in6_addr
*dst6
= &ro6
->ro_dst
.sin6_addr
;
580 struct ifnet
*fwd_ifp
= NULL
;
581 struct nd_prefix
*pr
;
584 if ((rt
= ro6
->ro_rt
) != NULL
) {
586 if (!(rt
->rt_flags
& RTF_PROXY
) || rt
->rt_ifp
== ifp
) {
587 nd6log2((LOG_DEBUG
, "%s: found incorrect prefix "
588 "proxy route for dst %s on %s\n", if_name(ifp
),
590 if_name(rt
->rt_ifp
)));
592 /* look it up below */
596 * The route is already marked with RTF_PRPROXY and
597 * it isn't pointing back to the inbound interface;
598 * optimistically return (see notes below).
605 * Find out where we should forward this packet to, by searching
606 * for another interface that is proxying for the prefix. Our
607 * current implementation assumes that the proxied prefix is shared
608 * to no more than one downstream interfaces (typically a bridge
611 lck_mtx_lock(nd6_mutex
);
612 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
613 struct in6_addr pr_addr
;
614 struct nd_prefix
*fwd
;
618 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
619 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
620 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
621 dst6
, &pr
->ndpr_mask
)) {
626 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
627 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
628 pr_len
= pr
->ndpr_plen
;
631 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
633 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
634 fwd
->ndpr_ifp
== ifp
||
635 fwd
->ndpr_plen
!= pr_len
||
636 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
642 fwd_ifp
= fwd
->ndpr_ifp
;
648 lck_mtx_unlock(nd6_mutex
);
650 lck_mtx_lock(rnh_lock
);
651 ROUTE_RELEASE_LOCKED(ro6
);
654 * Lookup a forwarding route; delete the route if it's incorrect,
655 * or return to caller if the correct one got created prior to
656 * our acquiring the rnh_lock.
658 if ((rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 0,
659 RTF_CLONING
| RTF_PRCLONING
, IFSCOPE_NONE
)) != NULL
) {
661 if (rt
->rt_ifp
!= fwd_ifp
|| !(rt
->rt_flags
& RTF_PROXY
)) {
662 rt
->rt_flags
|= RTF_CONDEMNED
;
664 (void) rtrequest_locked(RTM_DELETE
, rt_key(rt
),
665 rt
->rt_gateway
, rt_mask(rt
), rt
->rt_flags
, NULL
);
669 nd6log2((LOG_DEBUG
, "%s: found prefix proxy route "
670 "for dst %s\n", if_name(rt
->rt_ifp
),
673 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
674 lck_mtx_unlock(rnh_lock
);
678 VERIFY(rt
== NULL
&& ro6
->ro_rt
== NULL
);
681 * Clone a route from the correct parent prefix route and return it.
683 if (fwd_ifp
!= NULL
&& (rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 1,
684 RTF_PRCLONING
, fwd_ifp
->if_index
)) != NULL
) {
686 if (!(rt
->rt_flags
& RTF_PROXY
)) {
691 nd6log2((LOG_DEBUG
, "%s: allocated prefix proxy "
692 "route for dst %s\n", if_name(rt
->rt_ifp
),
695 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
698 VERIFY(rt
!= NULL
|| ro6
->ro_rt
== NULL
);
700 if (fwd_ifp
== NULL
|| rt
== NULL
) {
701 nd6log2((LOG_ERR
, "%s: failed to find forwarding prefix "
702 "proxy entry for dst %s\n", if_name(ifp
),
705 lck_mtx_unlock(rnh_lock
);
709 * Called when a prefix transitions between on-link and off-link. Perform
710 * routing (RTF_PROXY) and interface (all-multicast) related operations on
711 * the affected prefixes.
714 nd6_prproxy_prelist_update(struct nd_prefix
*pr_cur
, struct nd_prefix
*pr_up
)
716 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
717 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
718 struct nd6_prproxy_prelist
*up
, *down
;
719 struct nd_prefix
*pr
;
720 struct in6_addr pr_addr
;
724 SLIST_INIT(&up_head
);
725 SLIST_INIT(&down_head
);
726 VERIFY(pr_cur
!= NULL
);
728 lck_mtx_assert(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
731 * Upstream prefix. If caller did not specify one, search for one
732 * based on the information in current prefix. Caller is expected
733 * to have held an extra reference for the passed-in prefixes.
735 lck_mtx_lock(nd6_mutex
);
738 bcopy(&pr_cur
->ndpr_prefix
.sin6_addr
, &pr_addr
,
740 pr_len
= pr_cur
->ndpr_plen
;
743 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
745 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
746 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
747 pr
->ndpr_plen
!= pr_len
||
748 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
757 if ((pr_up
= pr
) == NULL
) {
758 lck_mtx_unlock(nd6_mutex
);
764 bcopy(&pr_up
->ndpr_prefix
.sin6_addr
, &pr_addr
,
766 pr_len
= pr_up
->ndpr_plen
;
768 NDPR_LOCK_ASSERT_HELD(pr_up
);
770 * Upstream prefix could be offlink by now; therefore we cannot
771 * assert that NDPRF_PRPROXY is set; however, we can insist that
772 * it must not be a scoped prefix.
774 VERIFY(!(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
));
775 enable
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
778 up
= nd6_ndprl_alloc(M_WAITOK
);
780 lck_mtx_unlock(nd6_mutex
);
785 up
->ndprl_pr
= pr_up
;
786 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
789 * Now build a list of matching (scoped) downstream prefixes on other
790 * interfaces which need to be enabled/disabled accordingly. Note that
791 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
793 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
795 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
796 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
797 pr
->ndpr_plen
!= pr_len
||
798 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
805 down
= nd6_ndprl_alloc(M_WAITOK
);
812 down
->ndprl_up
= pr_up
;
813 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
815 lck_mtx_unlock(nd6_mutex
);
818 * Apply routing function on prefixes; callee will free resources.
820 nd6_prproxy_prelist_setroute(enable
,
821 (struct nd6_prproxy_prelist_head
*)&up_head
,
822 (struct nd6_prproxy_prelist_head
*)&down_head
);
825 VERIFY(SLIST_EMPTY(&up_head
));
826 VERIFY(SLIST_EMPTY(&down_head
));
830 * Given an interface address, determine whether or not the address
831 * is part of of a proxied prefix.
834 nd6_prproxy_ifaddr(struct in6_ifaddr
*ia
)
836 struct nd_prefix
*pr
;
837 struct in6_addr addr
, pr_mask
;
839 boolean_t proxied
= FALSE
;
841 lck_mtx_assert(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
843 IFA_LOCK(&ia
->ia_ifa
);
844 bcopy(&ia
->ia_addr
.sin6_addr
, &addr
, sizeof (addr
));
845 bcopy(&ia
->ia_prefixmask
.sin6_addr
, &pr_mask
, sizeof (pr_mask
));
846 pr_len
= ia
->ia_plen
;
847 IFA_UNLOCK(&ia
->ia_ifa
);
849 lck_mtx_lock(nd6_mutex
);
850 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
852 if ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
853 (pr
->ndpr_stateflags
& NDPRF_PRPROXY
) &&
854 in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
862 lck_mtx_unlock(nd6_mutex
);
868 * Perform automatic proxy function with NS output.
870 * If the target address matches a global prefix obtained from a router
871 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
872 * flag set, then we send solicitations for the target address to all other
873 * interfaces where a matching prefix is currently on-link, in addition to
874 * the original interface.
877 nd6_prproxy_ns_output(struct ifnet
*ifp
, struct ifnet
*exclifp
,
878 struct in6_addr
*daddr
, struct in6_addr
*taddr
, struct llinfo_nd6
*ln
)
880 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
881 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
882 struct nd_prefix
*pr
, *fwd
;
883 struct ifnet
*fwd_ifp
;
884 struct in6_addr pr_addr
;
888 * Ignore excluded interface if it's the same as the original;
889 * we always send a NS on the original interface down below.
891 if (exclifp
!= NULL
&& exclifp
== ifp
)
895 nd6log2((LOG_DEBUG
, "%s: sending NS who has %s on ALL\n",
896 if_name(ifp
), ip6_sprintf(taddr
)));
898 nd6log2((LOG_DEBUG
, "%s: sending NS who has %s on ALL "
899 "(except %s)\n", if_name(ifp
),
900 ip6_sprintf(taddr
), if_name(exclifp
)));
902 SLIST_INIT(&ndprl_head
);
904 lck_mtx_lock(nd6_mutex
);
906 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
908 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
909 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
910 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
911 taddr
, &pr
->ndpr_mask
)) {
916 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
917 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
918 pr_len
= pr
->ndpr_plen
;
921 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
923 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
924 fwd
->ndpr_ifp
== ifp
|| fwd
->ndpr_ifp
== exclifp
||
925 fwd
->ndpr_plen
!= pr_len
||
926 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
932 fwd_ifp
= fwd
->ndpr_ifp
;
935 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
940 ndprl
->ndprl_pr
= fwd
;
941 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
943 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
948 lck_mtx_unlock(nd6_mutex
);
950 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
951 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
953 pr
= ndprl
->ndprl_pr
;
954 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
956 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
958 nd6_ndprl_free(ndprl
);
963 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
966 "%s: Sending cloned NS who has %s, originally "
967 "on %s\n", if_name(fwd_ifp
),
968 ip6_sprintf(taddr
), if_name(ifp
)));
970 nd6_ns_output(fwd_ifp
, daddr
, taddr
, NULL
, 0);
976 nd6_ndprl_free(ndprl
);
978 VERIFY(SLIST_EMPTY(&ndprl_head
));
980 nd6_ns_output(ifp
, daddr
, taddr
, ln
, 0);
984 * Perform automatic proxy function with NS input.
986 * If the target address matches a global prefix obtained from a router
987 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
988 * flag set, then we send solicitations for the target address to all other
989 * interfaces where a matching prefix is currently on-link.
992 nd6_prproxy_ns_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
993 char *lladdr
, int lladdrlen
, struct in6_addr
*daddr
, struct in6_addr
*taddr
)
995 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
996 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
997 struct nd_prefix
*pr
, *fwd
;
998 struct ifnet
*fwd_ifp
;
999 struct in6_addr pr_addr
;
1001 boolean_t solrec
= FALSE
;
1003 SLIST_INIT(&ndprl_head
);
1005 lck_mtx_lock(nd6_mutex
);
1007 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1009 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1010 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1011 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1012 taddr
, &pr
->ndpr_mask
)) {
1017 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1018 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
1019 pr_len
= pr
->ndpr_plen
;
1022 * If this is a NS for NUD/AR, record it so that we know
1023 * how to forward the NA reply later on (if/when it arrives.)
1024 * Give up if we fail to save the NS info.
1026 if ((solrec
= !IN6_IS_ADDR_UNSPECIFIED(saddr
)) &&
1027 !nd6_solsrc_enq(pr
, ifp
, saddr
, taddr
)) {
1030 break; /* bail out */
1035 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
1037 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1038 fwd
->ndpr_ifp
== ifp
||
1039 fwd
->ndpr_plen
!= pr_len
||
1040 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
1041 &pr_addr
, pr_len
)) {
1046 fwd_ifp
= fwd
->ndpr_ifp
;
1049 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1054 ndprl
->ndprl_pr
= fwd
;
1055 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1056 ndprl
->ndprl_sol
= solrec
;
1058 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1063 lck_mtx_unlock(nd6_mutex
);
1066 * If this is a recorded solicitation (NS for NUD/AR), create
1067 * or update the neighbor cache entry for the soliciting node.
1068 * Later on, when the NA reply arrives, we will need this cache
1069 * entry in order to send the NA back to the original solicitor.
1070 * Without a neighbor cache entry, we'd end up with an endless
1071 * cycle of NS ping-pong between the us (the proxy) and the node
1072 * which is soliciting for the address.
1075 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1076 nd6_cache_lladdr(ifp
, saddr
, lladdr
, lladdrlen
,
1077 ND_NEIGHBOR_SOLICIT
, 0);
1080 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1081 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1083 pr
= ndprl
->ndprl_pr
;
1084 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1086 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
1088 nd6_ndprl_free(ndprl
);
1093 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
1096 "%s: Forwarding NS (%s) from %s to %s who "
1097 "has %s, originally on %s\n", if_name(fwd_ifp
),
1098 ndprl
->ndprl_sol
? "NUD/AR" :
1099 "DAD", ip6_sprintf(saddr
), ip6_sprintf(daddr
),
1100 ip6_sprintf(taddr
), if_name(ifp
)));
1102 nd6_ns_output(fwd_ifp
, ndprl
->ndprl_sol
? taddr
: NULL
,
1103 taddr
, NULL
, !ndprl
->ndprl_sol
);
1109 nd6_ndprl_free(ndprl
);
1111 VERIFY(SLIST_EMPTY(&ndprl_head
));
1115 * Perform automatic proxy function with NA input.
1117 * If the target address matches a global prefix obtained from a router
1118 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES flag
1119 * set, then we send neighbor advertisements for the target address on all
1120 * other interfaces where a matching prefix is currently on link.
1123 nd6_prproxy_na_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
1124 struct in6_addr
*daddr0
, struct in6_addr
*taddr
, int flags
)
1126 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
1127 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
1128 struct nd_prefix
*pr
;
1129 struct ifnet
*fwd_ifp
;
1130 struct in6_addr daddr
;
1132 SLIST_INIT(&ndprl_head
);
1135 lck_mtx_lock(nd6_mutex
);
1137 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1139 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1140 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1141 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1142 taddr
, &pr
->ndpr_mask
)) {
1147 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1149 * If this is a NA for NUD, see if there is a record created
1150 * for the corresponding NS; upon success, we get back the
1151 * interface where the NS originally arrived on, as well as
1152 * the soliciting node's address. Give up if we can't find it.
1154 if (!IN6_IS_ADDR_MULTICAST(daddr0
)) {
1156 bzero(&daddr
, sizeof (daddr
));
1157 if (!nd6_solsrc_deq(pr
, taddr
, &daddr
, &fwd_ifp
)) {
1159 break; /* bail out */
1161 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
) && fwd_ifp
);
1164 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1166 break; /* bail out */
1168 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1169 ndprl
->ndprl_sol
= TRUE
;
1170 ndprl
->ndprl_sol_saddr
= *(&daddr
);
1172 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1174 struct nd_prefix
*fwd
;
1175 struct in6_addr pr_addr
;
1178 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
,
1180 pr_len
= pr
->ndpr_plen
;
1183 for (fwd
= nd_prefix
.lh_first
; fwd
;
1184 fwd
= fwd
->ndpr_next
) {
1186 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1187 fwd
->ndpr_ifp
== ifp
||
1188 fwd
->ndpr_plen
!= pr_len
||
1189 !in6_are_prefix_equal(
1190 &fwd
->ndpr_prefix
.sin6_addr
,
1191 &pr_addr
, pr_len
)) {
1196 fwd_ifp
= fwd
->ndpr_ifp
;
1199 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1204 ndprl
->ndprl_pr
= fwd
;
1205 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1207 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1213 lck_mtx_unlock(nd6_mutex
);
1215 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1218 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1220 pr
= ndprl
->ndprl_pr
;
1221 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1223 if (ndprl
->ndprl_sol
) {
1225 daddr
= *(&ndprl
->ndprl_sol_saddr
);
1226 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
));
1227 send_na
= (in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1232 send_na
= ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
1233 in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1238 if (!ndprl
->ndprl_sol
) {
1240 "%s: Forwarding NA (DAD) from %s to %s "
1241 "tgt is %s, originally on %s\n",
1243 ip6_sprintf(saddr
), ip6_sprintf(&daddr
),
1244 ip6_sprintf(taddr
), if_name(ifp
)));
1247 "%s: Forwarding NA (NUD/AR) from %s to "
1248 "%s (was %s) tgt is %s, originally on "
1249 "%s\n", if_name(fwd_ifp
),
1251 ip6_sprintf(&daddr
), ip6_sprintf(daddr0
),
1252 ip6_sprintf(taddr
), if_name(ifp
)));
1255 nd6_na_output(fwd_ifp
, &daddr
, taddr
, flags
, 1, NULL
);
1261 nd6_ndprl_free(ndprl
);
1263 VERIFY(SLIST_EMPTY(&ndprl_head
));
1266 static struct nd6_prproxy_solsrc
*
1267 nd6_solsrc_alloc(int how
)
1269 struct nd6_prproxy_solsrc
*ssrc
;
1271 ssrc
= (how
== M_WAITOK
) ? zalloc(solsrc_zone
) :
1272 zalloc_noblock(solsrc_zone
);
1274 bzero(ssrc
, solsrc_size
);
1280 nd6_solsrc_free(struct nd6_prproxy_solsrc
*ssrc
)
1282 zfree(solsrc_zone
, ssrc
);
1286 nd6_prproxy_sols_purge(struct nd_prefix
*pr
, u_int64_t max_stgt
)
1288 struct nd6_prproxy_soltgt
*soltgt
, *tmp
;
1289 u_int64_t expire
= (max_stgt
> 0) ? net_uptime() : 0;
1291 NDPR_LOCK_ASSERT_HELD(pr
);
1293 /* Either trim all or those that have expired or are idle */
1294 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1295 &pr
->ndpr_prproxy_sols
, tmp
) {
1296 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1297 if (expire
== 0 || soltgt
->soltgt_expire
<= expire
||
1298 soltgt
->soltgt_cnt
== 0) {
1299 pr
->ndpr_prproxy_sols_cnt
--;
1300 RB_REMOVE(prproxy_sols_tree
,
1301 &pr
->ndpr_prproxy_sols
, soltgt
);
1302 nd6_soltgt_free(soltgt
);
1306 if (max_stgt
== 0 || pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1307 VERIFY(max_stgt
!= 0 || (pr
->ndpr_prproxy_sols_cnt
== 0 &&
1308 RB_EMPTY(&pr
->ndpr_prproxy_sols
)));
1312 /* Brute force; mercilessly evict entries until we are under limit */
1313 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1314 &pr
->ndpr_prproxy_sols
, tmp
) {
1315 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1316 pr
->ndpr_prproxy_sols_cnt
--;
1317 RB_REMOVE(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1318 nd6_soltgt_free(soltgt
);
1319 if (pr
->ndpr_prproxy_sols_cnt
< max_stgt
)
1325 * Purges all solicitation records on a given prefix.
1326 * Caller is responsible for holding prefix lock.
1329 nd6_prproxy_sols_reap(struct nd_prefix
*pr
)
1331 nd6_prproxy_sols_purge(pr
, 0);
1335 * Purges expired or idle solicitation records on a given prefix.
1336 * Caller is responsible for holding prefix lock.
1339 nd6_prproxy_sols_prune(struct nd_prefix
*pr
, u_int32_t max_stgt
)
1341 nd6_prproxy_sols_purge(pr
, max_stgt
);
1345 * Enqueue a soliciation record in the target record of a prefix.
1348 nd6_solsrc_enq(struct nd_prefix
*pr
, struct ifnet
*ifp
,
1349 struct in6_addr
*saddr
, struct in6_addr
*taddr
)
1351 struct nd6_prproxy_soltgt find
, *soltgt
;
1352 struct nd6_prproxy_solsrc
*ssrc
;
1353 u_int32_t max_stgt
= nd6_max_tgt_sols
;
1354 u_int32_t max_ssrc
= nd6_max_src_sols
;
1356 NDPR_LOCK_ASSERT_HELD(pr
);
1357 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1358 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
|NDPRF_PRPROXY
)) ==
1359 (NDPRF_ONLINK
|NDPRF_PRPROXY
));
1360 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1362 ssrc
= nd6_solsrc_alloc(M_WAITOK
);
1366 ssrc
->solsrc_saddr
= *saddr
;
1367 ssrc
->solsrc_ifp
= ifp
;
1369 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1371 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1372 if (soltgt
== NULL
) {
1373 if (max_stgt
!= 0 && pr
->ndpr_prproxy_sols_cnt
>= max_stgt
) {
1374 VERIFY(!RB_EMPTY(&pr
->ndpr_prproxy_sols
));
1375 nd6_prproxy_sols_prune(pr
, max_stgt
);
1376 VERIFY(pr
->ndpr_prproxy_sols_cnt
< max_stgt
);
1379 soltgt
= nd6_soltgt_alloc(M_WAITOK
);
1380 if (soltgt
== NULL
) {
1381 nd6_solsrc_free(ssrc
);
1385 soltgt
->soltgt_key
.taddr
= *taddr
;
1386 VERIFY(soltgt
->soltgt_cnt
== 0);
1387 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1389 pr
->ndpr_prproxy_sols_cnt
++;
1390 VERIFY(pr
->ndpr_prproxy_sols_cnt
!= 0);
1391 RB_INSERT(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1394 if (max_ssrc
!= 0 && soltgt
->soltgt_cnt
>= max_ssrc
) {
1395 VERIFY(!TAILQ_EMPTY(&soltgt
->soltgt_q
));
1396 nd6_soltgt_prune(soltgt
, max_ssrc
);
1397 VERIFY(soltgt
->soltgt_cnt
< max_ssrc
);
1400 soltgt
->soltgt_cnt
++;
1401 VERIFY(soltgt
->soltgt_cnt
!= 0);
1402 TAILQ_INSERT_TAIL(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1403 if (soltgt
->soltgt_cnt
== 1)
1404 soltgt
->soltgt_expire
= net_uptime() + ND6_TGT_SOLS_EXPIRE
;
1410 * Dequeue a solicitation record from a target record of a prefix.
1413 nd6_solsrc_deq(struct nd_prefix
*pr
, struct in6_addr
*taddr
,
1414 struct in6_addr
*daddr
, struct ifnet
**ifp
)
1416 struct nd6_prproxy_soltgt find
, *soltgt
;
1417 struct nd6_prproxy_solsrc
*ssrc
;
1419 NDPR_LOCK_ASSERT_HELD(pr
);
1420 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1421 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
|NDPRF_PRPROXY
)) ==
1422 (NDPRF_ONLINK
|NDPRF_PRPROXY
));
1424 bzero(daddr
, sizeof (*daddr
));
1427 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1429 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1430 if (soltgt
== NULL
|| soltgt
->soltgt_cnt
== 0) {
1431 VERIFY(soltgt
== NULL
|| TAILQ_EMPTY(&soltgt
->soltgt_q
));
1435 VERIFY(soltgt
->soltgt_cnt
!= 0);
1436 --soltgt
->soltgt_cnt
;
1437 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1438 VERIFY(ssrc
!= NULL
);
1439 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1440 *daddr
= *(&ssrc
->solsrc_saddr
);
1441 *ifp
= ssrc
->solsrc_ifp
;
1442 nd6_solsrc_free(ssrc
);
1447 static struct nd6_prproxy_soltgt
*
1448 nd6_soltgt_alloc(int how
)
1450 struct nd6_prproxy_soltgt
*soltgt
;
1452 soltgt
= (how
== M_WAITOK
) ? zalloc(soltgt_zone
) :
1453 zalloc_noblock(soltgt_zone
);
1454 if (soltgt
!= NULL
) {
1455 bzero(soltgt
, soltgt_size
);
1456 TAILQ_INIT(&soltgt
->soltgt_q
);
1462 nd6_soltgt_free(struct nd6_prproxy_soltgt
*soltgt
)
1464 struct nd6_prproxy_solsrc
*ssrc
, *tssrc
;
1466 TAILQ_FOREACH_SAFE(ssrc
, &soltgt
->soltgt_q
, solsrc_tqe
, tssrc
) {
1467 VERIFY(soltgt
->soltgt_cnt
> 0);
1468 soltgt
->soltgt_cnt
--;
1469 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1470 nd6_solsrc_free(ssrc
);
1473 VERIFY(soltgt
->soltgt_cnt
== 0);
1474 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1476 zfree(soltgt_zone
, soltgt
);
1480 nd6_soltgt_prune(struct nd6_prproxy_soltgt
*soltgt
, u_int32_t max_ssrc
)
1482 while (soltgt
->soltgt_cnt
>= max_ssrc
) {
1483 struct nd6_prproxy_solsrc
*ssrc
;
1485 VERIFY(soltgt
->soltgt_cnt
!= 0);
1486 --soltgt
->soltgt_cnt
;
1487 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1488 VERIFY(ssrc
!= NULL
);
1489 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1490 nd6_solsrc_free(ssrc
);
1495 * Solicited target tree comparison function.
1497 * An ordered predicate is necessary; bcmp() is not documented to return
1498 * an indication of order, memcmp() is, and is an ISO C99 requirement.
1501 soltgt_cmp(const struct nd6_prproxy_soltgt
*a
,
1502 const struct nd6_prproxy_soltgt
*b
)
1504 return (memcmp(&a
->soltgt_key
, &b
->soltgt_key
, sizeof (a
->soltgt_key
)));