2 * Copyright (c) 2011-2020 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(zalloc_flags_t
);
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 ZONE_DECLARE(ndprl_zone
, "nd6_prproxy_prelist",
181 sizeof(struct nd6_prproxy_prelist
), ZC_ZFREE_CLEARMEM
); /* nd6_prproxy_prelist zone */
183 static ZONE_DECLARE(solsrc_zone
, "nd6_prproxy_solsrc",
184 sizeof(struct nd6_prproxy_solsrc
), ZC_ZFREE_CLEARMEM
); /* nd6_prproxy_solsrc zone */
186 static ZONE_DECLARE(soltgt_zone
, "nd6_prproxy_soltgt",
187 sizeof(struct nd6_prproxy_soltgt
), ZC_ZFREE_CLEARMEM
); /* nd6_prproxy_soltgt zone */
189 /* The following is protected by ndpr_lock */
190 RB_GENERATE_PREV(prproxy_sols_tree
, nd6_prproxy_soltgt
,
191 soltgt_link
, soltgt_cmp
);
193 /* The following is protected by proxy6_lock (for updates) */
194 u_int32_t nd6_prproxy
;
196 extern lck_mtx_t
*nd6_mutex
;
198 SYSCTL_DECL(_net_inet6_icmp6
);
200 SYSCTL_UINT(_net_inet6_icmp6
, OID_AUTO
, nd6_maxsolstgt
,
201 CTLFLAG_RW
| CTLFLAG_LOCKED
, &nd6_max_tgt_sols
, ND6_MAX_TGT_SOLS_DEFAULT
,
202 "maximum number of outstanding solicited targets per prefix");
204 SYSCTL_UINT(_net_inet6_icmp6
, OID_AUTO
, nd6_maxproxiedsol
,
205 CTLFLAG_RW
| CTLFLAG_LOCKED
, &nd6_max_src_sols
, ND6_MAX_SRC_SOLS_DEFAULT
,
206 "maximum number of outstanding solicitations per target");
208 SYSCTL_UINT(_net_inet6_icmp6
, OID_AUTO
, prproxy_cnt
,
209 CTLFLAG_RD
| CTLFLAG_LOCKED
, &nd6_prproxy
, 0,
210 "total number of proxied prefixes");
212 static struct nd6_prproxy_prelist
*
213 nd6_ndprl_alloc(zalloc_flags_t how
)
215 return zalloc_flags(ndprl_zone
, how
| Z_ZERO
);
219 nd6_ndprl_free(struct nd6_prproxy_prelist
*ndprl
)
221 zfree(ndprl_zone
, ndprl
);
225 * Apply routing function on the affected upstream and downstream prefixes,
226 * i.e. either set or clear RTF_PROXY on the cloning prefix route; all route
227 * entries that were cloned off these prefixes will be blown away. Caller
228 * must have acquired proxy6_lock and must not be holding nd6_mutex.
231 nd6_prproxy_prelist_setroute(boolean_t enable
,
232 struct nd6_prproxy_prelist_head
*up_head
,
233 struct nd6_prproxy_prelist_head
*down_head
)
235 struct nd6_prproxy_prelist
*up
, *down
, *ndprl_tmp
;
236 struct nd_prefix
*pr
;
238 LCK_MTX_ASSERT(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
239 LCK_MTX_ASSERT(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
241 SLIST_FOREACH_SAFE(up
, up_head
, ndprl_le
, ndprl_tmp
) {
243 boolean_t prproxy
, set_allmulti
= FALSE
;
244 int allmulti_sw
= FALSE
;
245 struct ifnet
*ifp
= NULL
;
247 SLIST_REMOVE(up_head
, up
, nd6_prproxy_prelist
, ndprl_le
);
249 VERIFY(up
->ndprl_up
== NULL
);
253 prproxy
= (pr
->ndpr_stateflags
& NDPRF_PRPROXY
);
254 VERIFY(!prproxy
|| ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
255 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
257 nd6_prproxy_sols_reap(pr
);
258 VERIFY(pr
->ndpr_prproxy_sols_cnt
== 0);
259 VERIFY(RB_EMPTY(&pr
->ndpr_prproxy_sols
));
261 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
263 pr
->ndpr_allmulti_cnt
++;
266 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
268 pr
->ndpr_allmulti_cnt
--;
273 if ((rt
= pr
->ndpr_rt
) != NULL
) {
274 if ((enable
&& prproxy
) || (!enable
&& !prproxy
)) {
284 /* Call the following ioctl after releasing NDPR lock */
285 if (set_allmulti
&& ifp
!= NULL
) {
286 if_allmulti(ifp
, allmulti_sw
);
292 rt_set_proxy(rt
, enable
);
298 SLIST_FOREACH_SAFE(down
, down_head
, ndprl_le
, ndprl_tmp
) {
299 struct nd_prefix
*pr_up
;
301 boolean_t prproxy
, set_allmulti
= FALSE
;
302 int allmulti_sw
= FALSE
;
303 struct ifnet
*ifp
= NULL
;
305 SLIST_REMOVE(down_head
, down
, nd6_prproxy_prelist
, ndprl_le
);
307 pr_up
= down
->ndprl_up
;
308 VERIFY(pr_up
!= NULL
);
312 prproxy
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
313 VERIFY(!prproxy
|| ((pr_up
->ndpr_stateflags
& NDPRF_ONLINK
) &&
314 !(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
)));
318 if (enable
&& pr
->ndpr_allmulti_cnt
== 0) {
319 pr
->ndpr_allmulti_cnt
++;
322 } else if (!enable
&& pr
->ndpr_allmulti_cnt
> 0) {
323 pr
->ndpr_allmulti_cnt
--;
328 if ((rt
= pr
->ndpr_rt
) != NULL
) {
329 if ((enable
&& prproxy
) || (!enable
&& !prproxy
)) {
338 if (set_allmulti
&& ifp
!= NULL
) {
339 if_allmulti(ifp
, allmulti_sw
);
345 rt_set_proxy(rt
, enable
);
348 nd6_ndprl_free(down
);
353 * Enable/disable prefix proxying on an interface; typically called
354 * as part of handling SIOCSIFINFO_FLAGS[SETROUTERMODE_IN6]
357 nd6_if_prproxy(struct ifnet
*ifp
, boolean_t enable
)
359 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
360 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
361 struct nd6_prproxy_prelist
*up
, *down
;
362 struct nd_prefix
*pr
;
364 /* Can't be enabled if we are an advertising router on the interface */
365 ifnet_lock_shared(ifp
);
366 if (enable
&& (ifp
->if_ipv6_router_mode
== IPV6_ROUTER_MODE_EXCLUSIVE
)) {
367 ifnet_lock_done(ifp
);
370 ifnet_lock_done(ifp
);
372 SLIST_INIT(&up_head
);
373 SLIST_INIT(&down_head
);
376 * Serialize the clearing/setting of NDPRF_PRPROXY.
378 lck_mtx_lock(&proxy6_lock
);
381 * First build a list of upstream prefixes on this interface for
382 * which we need to enable/disable prefix proxy functionality.
384 lck_mtx_lock(nd6_mutex
);
385 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
387 if (IN6_IS_ADDR_LINKLOCAL(&pr
->ndpr_prefix
.sin6_addr
) ||
388 (!enable
&& !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
389 (enable
&& (pr
->ndpr_stateflags
& NDPRF_PRPROXY
)) ||
390 (pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
391 pr
->ndpr_ifp
!= ifp
) {
397 * At present, in order for the prefix to be eligible
398 * as a proxying/proxied prefix, we require that the
399 * prefix route entry be marked as a cloning route with
400 * RTF_PROXY; i.e. nd6_need_cache() needs to return
401 * true for the interface type.
403 if (enable
&& (pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
404 nd6_need_cache(ifp
)) {
405 pr
->ndpr_stateflags
|= NDPRF_PRPROXY
;
408 } else if (!enable
) {
409 pr
->ndpr_stateflags
&= ~NDPRF_PRPROXY
;
414 pr
= NULL
; /* don't go further */
421 up
= nd6_ndprl_alloc(Z_WAITOK
);
427 up
->ndprl_pr
= pr
; /* keep reference from above */
428 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
432 * Now build a list of matching (scoped) downstream prefixes on other
433 * interfaces which need to be enabled/disabled accordingly. Note that
434 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
436 SLIST_FOREACH(up
, &up_head
, ndprl_le
) {
437 struct nd_prefix
*fwd
;
438 struct in6_addr pr_addr
;
444 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
445 pr_len
= pr
->ndpr_plen
;
448 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
450 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
451 !(fwd
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
452 fwd
->ndpr_plen
!= pr_len
||
453 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
460 down
= nd6_ndprl_alloc(Z_WAITOK
);
466 down
->ndprl_pr
= fwd
;
469 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
472 lck_mtx_unlock(nd6_mutex
);
475 * Apply routing function on prefixes; callee will free resources.
477 nd6_prproxy_prelist_setroute(enable
,
478 (struct nd6_prproxy_prelist_head
*)&up_head
,
479 (struct nd6_prproxy_prelist_head
*)&down_head
);
481 VERIFY(SLIST_EMPTY(&up_head
));
482 VERIFY(SLIST_EMPTY(&down_head
));
484 lck_mtx_unlock(&proxy6_lock
);
490 * Called from the input path to determine whether the packet is destined
491 * to a proxied node; if so, mark the mbuf with PKTFF_PROXY_DST so that
492 * icmp6_input() knows that this is not to be delivered to socket(s).
495 nd6_prproxy_isours(struct mbuf
*m
, struct ip6_hdr
*ip6
, struct route_in6
*ro6
,
496 unsigned int ifscope
)
499 boolean_t ours
= FALSE
;
501 if (ip6
->ip6_hlim
!= IPV6_MAXHLIM
|| ip6
->ip6_nxt
!= IPPROTO_ICMPV6
) {
505 if (IN6_IS_ADDR_MC_NODELOCAL(&ip6
->ip6_dst
) ||
506 IN6_IS_ADDR_MC_LINKLOCAL(&ip6
->ip6_dst
)) {
510 } else if (IN6_IS_ADDR_MULTICAST(&ip6
->ip6_dst
)) {
518 if ((rt
= ro6
->ro_rt
) != NULL
) {
522 if (ROUTE_UNUSABLE(ro6
)) {
529 /* Caller must have ensured this condition (not srcrt) */
530 VERIFY(IN6_ARE_ADDR_EQUAL(&ip6
->ip6_dst
,
531 &ro6
->ro_dst
.sin6_addr
));
533 rtalloc_scoped_ign((struct route
*)ro6
, RTF_PRCLONING
, ifscope
);
534 if ((rt
= ro6
->ro_rt
) == NULL
) {
541 ours
= (rt
->rt_flags
& RTF_PROXY
) ? TRUE
: FALSE
;
546 m
->m_pkthdr
.pkt_flags
|= PKTF_PROXY_DST
;
553 * Called from the input path to determine whether or not the proxy
554 * route entry is pointing to the correct interface, and to perform
555 * the necessary route fixups otherwise.
558 nd6_proxy_find_fwdroute(struct ifnet
*ifp
, struct route_in6
*ro6
)
560 struct in6_addr
*dst6
= &ro6
->ro_dst
.sin6_addr
;
561 struct ifnet
*fwd_ifp
= NULL
;
562 struct nd_prefix
*pr
;
565 if ((rt
= ro6
->ro_rt
) != NULL
) {
567 if (!(rt
->rt_flags
& RTF_PROXY
) || rt
->rt_ifp
== ifp
) {
568 nd6log2(debug
, "%s: found incorrect prefix "
569 "proxy route for dst %s on %s\n", if_name(ifp
),
571 if_name(rt
->rt_ifp
));
573 /* look it up below */
577 * The route is already marked with RTF_PRPROXY and
578 * it isn't pointing back to the inbound interface;
579 * optimistically return (see notes below).
586 * Find out where we should forward this packet to, by searching
587 * for another interface that is proxying for the prefix. Our
588 * current implementation assumes that the proxied prefix is shared
589 * to no more than one downstream interfaces (typically a bridge
592 lck_mtx_lock(nd6_mutex
);
593 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
594 struct in6_addr pr_addr
;
595 struct nd_prefix
*fwd
;
599 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
600 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
601 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
602 dst6
, &pr
->ndpr_mask
)) {
607 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
608 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
609 pr_len
= pr
->ndpr_plen
;
612 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
614 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
615 fwd
->ndpr_ifp
== ifp
||
616 fwd
->ndpr_plen
!= pr_len
||
617 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
623 fwd_ifp
= fwd
->ndpr_ifp
;
629 lck_mtx_unlock(nd6_mutex
);
631 lck_mtx_lock(rnh_lock
);
632 ROUTE_RELEASE_LOCKED(ro6
);
635 * Lookup a forwarding route; delete the route if it's incorrect,
636 * or return to caller if the correct one got created prior to
637 * our acquiring the rnh_lock.
639 if ((rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 0,
640 RTF_CLONING
| RTF_PRCLONING
, IFSCOPE_NONE
)) != NULL
) {
642 if (rt
->rt_ifp
!= fwd_ifp
|| !(rt
->rt_flags
& RTF_PROXY
)) {
643 rt
->rt_flags
|= RTF_CONDEMNED
;
645 (void) rtrequest_locked(RTM_DELETE
, rt_key(rt
),
646 rt
->rt_gateway
, rt_mask(rt
), rt
->rt_flags
, NULL
);
650 nd6log2(debug
, "%s: found prefix proxy route "
651 "for dst %s\n", if_name(rt
->rt_ifp
),
654 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
655 lck_mtx_unlock(rnh_lock
);
659 VERIFY(rt
== NULL
&& ro6
->ro_rt
== NULL
);
662 * Clone a route from the correct parent prefix route and return it.
664 if (fwd_ifp
!= NULL
&& (rt
= rtalloc1_scoped_locked(SA(&ro6
->ro_dst
), 1,
665 RTF_PRCLONING
, fwd_ifp
->if_index
)) != NULL
) {
667 if (!(rt
->rt_flags
& RTF_PROXY
)) {
672 nd6log2(debug
, "%s: allocated prefix proxy "
673 "route for dst %s\n", if_name(rt
->rt_ifp
),
676 ro6
->ro_rt
= rt
; /* refcnt held by rtalloc1 */
679 VERIFY(rt
!= NULL
|| ro6
->ro_rt
== NULL
);
681 if (fwd_ifp
== NULL
|| rt
== NULL
) {
682 nd6log2(error
, "%s: failed to find forwarding prefix "
683 "proxy entry for dst %s\n", if_name(ifp
),
686 lck_mtx_unlock(rnh_lock
);
690 * Called when a prefix transitions between on-link and off-link. Perform
691 * routing (RTF_PROXY) and interface (all-multicast) related operations on
692 * the affected prefixes.
695 nd6_prproxy_prelist_update(struct nd_prefix
*pr_cur
, struct nd_prefix
*pr_up
)
697 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
698 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
699 struct nd6_prproxy_prelist
*up
, *down
;
700 struct nd_prefix
*pr
;
701 struct in6_addr pr_addr
;
705 SLIST_INIT(&up_head
);
706 SLIST_INIT(&down_head
);
707 VERIFY(pr_cur
!= NULL
);
709 LCK_MTX_ASSERT(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
712 * Upstream prefix. If caller did not specify one, search for one
713 * based on the information in current prefix. Caller is expected
714 * to have held an extra reference for the passed-in prefixes.
716 lck_mtx_lock(nd6_mutex
);
719 bcopy(&pr_cur
->ndpr_prefix
.sin6_addr
, &pr_addr
,
721 pr_len
= pr_cur
->ndpr_plen
;
724 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
726 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
727 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
728 pr
->ndpr_plen
!= pr_len
||
729 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
738 if ((pr_up
= pr
) == NULL
) {
739 lck_mtx_unlock(nd6_mutex
);
745 bcopy(&pr_up
->ndpr_prefix
.sin6_addr
, &pr_addr
,
747 pr_len
= pr_up
->ndpr_plen
;
749 NDPR_LOCK_ASSERT_HELD(pr_up
);
751 * Upstream prefix could be offlink by now; therefore we cannot
752 * assert that NDPRF_PRPROXY is set; however, we can insist that
753 * it must not be a scoped prefix.
755 VERIFY(!(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
));
756 enable
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
759 up
= nd6_ndprl_alloc(Z_WAITOK
);
761 lck_mtx_unlock(nd6_mutex
);
766 up
->ndprl_pr
= pr_up
;
767 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
770 * Now build a list of matching (scoped) downstream prefixes on other
771 * interfaces which need to be enabled/disabled accordingly. Note that
772 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
774 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
776 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
777 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
778 pr
->ndpr_plen
!= pr_len
||
779 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
786 down
= nd6_ndprl_alloc(Z_WAITOK
);
794 down
->ndprl_up
= pr_up
;
795 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
797 lck_mtx_unlock(nd6_mutex
);
800 * Apply routing function on prefixes; callee will free resources.
802 nd6_prproxy_prelist_setroute(enable
,
803 (struct nd6_prproxy_prelist_head
*)&up_head
,
804 (struct nd6_prproxy_prelist_head
*)&down_head
);
807 VERIFY(SLIST_EMPTY(&up_head
));
808 VERIFY(SLIST_EMPTY(&down_head
));
812 * Given an interface address, determine whether or not the address
813 * is part of of a proxied prefix.
816 nd6_prproxy_ifaddr(struct in6_ifaddr
*ia
)
818 struct nd_prefix
*pr
;
819 struct in6_addr addr
, pr_mask
;
821 boolean_t proxied
= FALSE
;
823 LCK_MTX_ASSERT(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
825 IFA_LOCK(&ia
->ia_ifa
);
826 bcopy(&ia
->ia_addr
.sin6_addr
, &addr
, sizeof(addr
));
827 bcopy(&ia
->ia_prefixmask
.sin6_addr
, &pr_mask
, sizeof(pr_mask
));
828 pr_len
= ia
->ia_plen
;
829 IFA_UNLOCK(&ia
->ia_ifa
);
831 lck_mtx_lock(nd6_mutex
);
832 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
834 if ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
835 (pr
->ndpr_stateflags
& NDPRF_PRPROXY
) &&
836 in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
844 lck_mtx_unlock(nd6_mutex
);
850 * Perform automatic proxy function with NS output.
852 * If the target address matches a global prefix obtained from a router
853 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
854 * flag set, then we send solicitations for the target address to all other
855 * interfaces where a matching prefix is currently on-link, in addition to
856 * the original interface.
859 nd6_prproxy_ns_output(struct ifnet
*ifp
, struct ifnet
*exclifp
,
860 struct in6_addr
*daddr
, struct in6_addr
*taddr
, struct llinfo_nd6
*ln
)
862 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
863 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
864 struct nd_prefix
*pr
, *fwd
;
865 struct ifnet
*fwd_ifp
;
866 struct in6_addr pr_addr
;
870 * Ignore excluded interface if it's the same as the original;
871 * we always send a NS on the original interface down below.
873 if (exclifp
!= NULL
&& exclifp
== ifp
) {
877 if (exclifp
== NULL
) {
878 nd6log2(debug
, "%s: sending NS who has %s on ALL\n",
879 if_name(ifp
), ip6_sprintf(taddr
));
881 nd6log2(debug
, "%s: sending NS who has %s on ALL "
882 "(except %s)\n", if_name(ifp
),
883 ip6_sprintf(taddr
), if_name(exclifp
));
886 SLIST_INIT(&ndprl_head
);
888 lck_mtx_lock(nd6_mutex
);
890 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
892 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
893 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
894 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
895 taddr
, &pr
->ndpr_mask
)) {
900 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
901 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
902 pr_len
= pr
->ndpr_plen
;
905 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
907 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
908 fwd
->ndpr_ifp
== ifp
|| fwd
->ndpr_ifp
== exclifp
||
909 fwd
->ndpr_plen
!= pr_len
||
910 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
916 fwd_ifp
= fwd
->ndpr_ifp
;
919 ndprl
= nd6_ndprl_alloc(Z_WAITOK
);
925 ndprl
->ndprl_pr
= fwd
;
926 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
928 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
933 lck_mtx_unlock(nd6_mutex
);
935 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
936 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
938 pr
= ndprl
->ndprl_pr
;
939 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
941 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
943 nd6_ndprl_free(ndprl
);
948 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
951 "%s: Sending cloned NS who has %s, originally "
952 "on %s\n", if_name(fwd_ifp
),
953 ip6_sprintf(taddr
), if_name(ifp
));
955 nd6_ns_output(fwd_ifp
, daddr
, taddr
, NULL
, NULL
);
961 nd6_ndprl_free(ndprl
);
963 VERIFY(SLIST_EMPTY(&ndprl_head
));
965 nd6_ns_output(ifp
, daddr
, taddr
, ln
, NULL
);
969 * Perform automatic proxy function with NS input.
971 * If the target address matches a global prefix obtained from a router
972 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
973 * flag set, then we send solicitations for the target address to all other
974 * interfaces where a matching prefix is currently on-link.
977 nd6_prproxy_ns_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
978 char *lladdr
, int lladdrlen
, struct in6_addr
*daddr
,
979 struct in6_addr
*taddr
, uint8_t *nonce
)
981 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
982 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
983 struct nd_prefix
*pr
, *fwd
;
984 struct ifnet
*fwd_ifp
;
985 struct in6_addr pr_addr
;
987 boolean_t solrec
= FALSE
;
989 SLIST_INIT(&ndprl_head
);
991 lck_mtx_lock(nd6_mutex
);
993 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
995 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
996 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
997 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
998 taddr
, &pr
->ndpr_mask
)) {
1003 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1004 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof(pr_addr
));
1005 pr_len
= pr
->ndpr_plen
;
1008 * If this is a NS for NUD/AR, record it so that we know
1009 * how to forward the NA reply later on (if/when it arrives.)
1010 * Give up if we fail to save the NS info.
1012 if ((solrec
= !IN6_IS_ADDR_UNSPECIFIED(saddr
)) &&
1013 !nd6_solsrc_enq(pr
, ifp
, saddr
, taddr
)) {
1016 break; /* bail out */
1021 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
1023 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1024 fwd
->ndpr_ifp
== ifp
||
1025 fwd
->ndpr_plen
!= pr_len
||
1026 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
1027 &pr_addr
, pr_len
)) {
1032 fwd_ifp
= fwd
->ndpr_ifp
;
1035 ndprl
= nd6_ndprl_alloc(Z_WAITOK
);
1036 if (ndprl
== NULL
) {
1041 ndprl
->ndprl_pr
= fwd
;
1042 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1043 ndprl
->ndprl_sol
= solrec
;
1045 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1050 lck_mtx_unlock(nd6_mutex
);
1053 * If this is a recorded solicitation (NS for NUD/AR), create
1054 * or update the neighbor cache entry for the soliciting node.
1055 * Later on, when the NA reply arrives, we will need this cache
1056 * entry in order to send the NA back to the original solicitor.
1057 * Without a neighbor cache entry, we'd end up with an endless
1058 * cycle of NS ping-pong between the us (the proxy) and the node
1059 * which is soliciting for the address.
1062 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1063 nd6_cache_lladdr(ifp
, saddr
, lladdr
, lladdrlen
,
1064 ND_NEIGHBOR_SOLICIT
, 0);
1067 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1068 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1070 pr
= ndprl
->ndprl_pr
;
1071 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1073 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
1075 nd6_ndprl_free(ndprl
);
1080 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
1083 "%s: Forwarding NS (%s) from %s to %s who "
1084 "has %s, originally on %s\n", if_name(fwd_ifp
),
1085 ndprl
->ndprl_sol
? "NUD/AR" :
1086 "DAD", ip6_sprintf(saddr
), ip6_sprintf(daddr
),
1087 ip6_sprintf(taddr
), if_name(ifp
));
1089 nd6_ns_output(fwd_ifp
, ndprl
->ndprl_sol
? taddr
: NULL
,
1090 taddr
, NULL
, nonce
);
1096 nd6_ndprl_free(ndprl
);
1098 VERIFY(SLIST_EMPTY(&ndprl_head
));
1102 * Perform automatic proxy function with NA input.
1104 * If the target address matches a global prefix obtained from a router
1105 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES flag
1106 * set, then we send neighbor advertisements for the target address on all
1107 * other interfaces where a matching prefix is currently on link.
1110 nd6_prproxy_na_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
1111 struct in6_addr
*daddr0
, struct in6_addr
*taddr
, int flags
)
1113 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
1114 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
1115 struct nd_prefix
*pr
;
1116 struct ifnet
*fwd_ifp
;
1117 struct in6_addr daddr
;
1119 SLIST_INIT(&ndprl_head
);
1122 lck_mtx_lock(nd6_mutex
);
1124 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
1126 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1127 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
1128 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
1129 taddr
, &pr
->ndpr_mask
)) {
1134 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1136 * If this is a NA for NUD, see if there is a record created
1137 * for the corresponding NS; upon success, we get back the
1138 * interface where the NS originally arrived on, as well as
1139 * the soliciting node's address. Give up if we can't find it.
1141 if (!IN6_IS_ADDR_MULTICAST(daddr0
)) {
1143 bzero(&daddr
, sizeof(daddr
));
1144 if (!nd6_solsrc_deq(pr
, taddr
, &daddr
, &fwd_ifp
)) {
1146 break; /* bail out */
1148 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
) && fwd_ifp
);
1151 ndprl
= nd6_ndprl_alloc(Z_WAITOK
);
1152 if (ndprl
== NULL
) {
1153 break; /* bail out */
1155 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1156 ndprl
->ndprl_sol
= TRUE
;
1157 ndprl
->ndprl_sol_saddr
= *(&daddr
);
1159 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1161 struct nd_prefix
*fwd
;
1162 struct in6_addr pr_addr
;
1165 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
,
1167 pr_len
= pr
->ndpr_plen
;
1170 for (fwd
= nd_prefix
.lh_first
; fwd
;
1171 fwd
= fwd
->ndpr_next
) {
1173 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
1174 fwd
->ndpr_ifp
== ifp
||
1175 fwd
->ndpr_plen
!= pr_len
||
1176 !in6_are_prefix_equal(
1177 &fwd
->ndpr_prefix
.sin6_addr
,
1178 &pr_addr
, pr_len
)) {
1183 fwd_ifp
= fwd
->ndpr_ifp
;
1186 ndprl
= nd6_ndprl_alloc(Z_WAITOK
);
1187 if (ndprl
== NULL
) {
1192 ndprl
->ndprl_pr
= fwd
;
1193 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1195 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1201 lck_mtx_unlock(nd6_mutex
);
1203 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1206 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1208 pr
= ndprl
->ndprl_pr
;
1209 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1211 if (ndprl
->ndprl_sol
) {
1213 daddr
= *(&ndprl
->ndprl_sol_saddr
);
1214 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
));
1215 send_na
= (in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1220 send_na
= ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
1221 in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1226 if (!ndprl
->ndprl_sol
) {
1228 "%s: Forwarding NA (DAD) from %s to %s "
1229 "tgt is %s, originally on %s\n",
1231 ip6_sprintf(saddr
), ip6_sprintf(&daddr
),
1232 ip6_sprintf(taddr
), if_name(ifp
));
1235 "%s: Forwarding NA (NUD/AR) from %s to "
1236 "%s (was %s) tgt is %s, originally on "
1237 "%s\n", if_name(fwd_ifp
),
1239 ip6_sprintf(&daddr
), ip6_sprintf(daddr0
),
1240 ip6_sprintf(taddr
), if_name(ifp
));
1243 nd6_na_output(fwd_ifp
, &daddr
, taddr
, flags
, 1, NULL
);
1250 nd6_ndprl_free(ndprl
);
1252 VERIFY(SLIST_EMPTY(&ndprl_head
));
1255 static struct nd6_prproxy_solsrc
*
1256 nd6_solsrc_alloc(int how
)
1258 return zalloc_flags(solsrc_zone
, how
| Z_ZERO
);
1262 nd6_solsrc_free(struct nd6_prproxy_solsrc
*ssrc
)
1264 zfree(solsrc_zone
, ssrc
);
1268 nd6_prproxy_sols_purge(struct nd_prefix
*pr
, u_int64_t max_stgt
)
1270 struct nd6_prproxy_soltgt
*soltgt
, *tmp
;
1271 u_int64_t expire
= (max_stgt
> 0) ? net_uptime() : 0;
1273 NDPR_LOCK_ASSERT_HELD(pr
);
1275 /* Either trim all or those that have expired or are idle */
1276 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1277 &pr
->ndpr_prproxy_sols
, tmp
) {
1278 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1279 if (expire
== 0 || soltgt
->soltgt_expire
<= expire
||
1280 soltgt
->soltgt_cnt
== 0) {
1281 pr
->ndpr_prproxy_sols_cnt
--;
1282 RB_REMOVE(prproxy_sols_tree
,
1283 &pr
->ndpr_prproxy_sols
, soltgt
);
1284 nd6_soltgt_free(soltgt
);
1288 if (max_stgt
== 0 || pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1289 VERIFY(max_stgt
!= 0 || (pr
->ndpr_prproxy_sols_cnt
== 0 &&
1290 RB_EMPTY(&pr
->ndpr_prproxy_sols
)));
1294 /* Brute force; mercilessly evict entries until we are under limit */
1295 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1296 &pr
->ndpr_prproxy_sols
, tmp
) {
1297 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1298 pr
->ndpr_prproxy_sols_cnt
--;
1299 RB_REMOVE(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1300 nd6_soltgt_free(soltgt
);
1301 if (pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1308 * Purges all solicitation records on a given prefix.
1309 * Caller is responsible for holding prefix lock.
1312 nd6_prproxy_sols_reap(struct nd_prefix
*pr
)
1314 nd6_prproxy_sols_purge(pr
, 0);
1318 * Purges expired or idle solicitation records on a given prefix.
1319 * Caller is responsible for holding prefix lock.
1322 nd6_prproxy_sols_prune(struct nd_prefix
*pr
, u_int32_t max_stgt
)
1324 nd6_prproxy_sols_purge(pr
, max_stgt
);
1328 * Enqueue a soliciation record in the target record of a prefix.
1331 nd6_solsrc_enq(struct nd_prefix
*pr
, struct ifnet
*ifp
,
1332 struct in6_addr
*saddr
, struct in6_addr
*taddr
)
1334 struct nd6_prproxy_soltgt find
, *soltgt
;
1335 struct nd6_prproxy_solsrc
*ssrc
;
1336 u_int32_t max_stgt
= nd6_max_tgt_sols
;
1337 u_int32_t max_ssrc
= nd6_max_src_sols
;
1339 NDPR_LOCK_ASSERT_HELD(pr
);
1340 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1341 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
| NDPRF_PRPROXY
)) ==
1342 (NDPRF_ONLINK
| NDPRF_PRPROXY
));
1343 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1345 ssrc
= nd6_solsrc_alloc(M_WAITOK
);
1350 ssrc
->solsrc_saddr
= *saddr
;
1351 ssrc
->solsrc_ifp
= ifp
;
1353 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1355 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1356 if (soltgt
== NULL
) {
1357 if (max_stgt
!= 0 && pr
->ndpr_prproxy_sols_cnt
>= max_stgt
) {
1358 VERIFY(!RB_EMPTY(&pr
->ndpr_prproxy_sols
));
1359 nd6_prproxy_sols_prune(pr
, max_stgt
);
1360 VERIFY(pr
->ndpr_prproxy_sols_cnt
< max_stgt
);
1363 soltgt
= nd6_soltgt_alloc(M_WAITOK
);
1364 if (soltgt
== NULL
) {
1365 nd6_solsrc_free(ssrc
);
1369 soltgt
->soltgt_key
.taddr
= *taddr
;
1370 VERIFY(soltgt
->soltgt_cnt
== 0);
1371 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1373 pr
->ndpr_prproxy_sols_cnt
++;
1374 VERIFY(pr
->ndpr_prproxy_sols_cnt
!= 0);
1375 RB_INSERT(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1378 if (max_ssrc
!= 0 && soltgt
->soltgt_cnt
>= max_ssrc
) {
1379 VERIFY(!TAILQ_EMPTY(&soltgt
->soltgt_q
));
1380 nd6_soltgt_prune(soltgt
, max_ssrc
);
1381 VERIFY(soltgt
->soltgt_cnt
< max_ssrc
);
1384 soltgt
->soltgt_cnt
++;
1385 VERIFY(soltgt
->soltgt_cnt
!= 0);
1386 TAILQ_INSERT_TAIL(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1387 if (soltgt
->soltgt_cnt
== 1) {
1388 soltgt
->soltgt_expire
= net_uptime() + ND6_TGT_SOLS_EXPIRE
;
1395 * Dequeue a solicitation record from a target record of a prefix.
1398 nd6_solsrc_deq(struct nd_prefix
*pr
, struct in6_addr
*taddr
,
1399 struct in6_addr
*daddr
, struct ifnet
**ifp
)
1401 struct nd6_prproxy_soltgt find
, *soltgt
;
1402 struct nd6_prproxy_solsrc
*ssrc
;
1404 NDPR_LOCK_ASSERT_HELD(pr
);
1405 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1406 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
| NDPRF_PRPROXY
)) ==
1407 (NDPRF_ONLINK
| NDPRF_PRPROXY
));
1409 bzero(daddr
, sizeof(*daddr
));
1412 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1414 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1415 if (soltgt
== NULL
|| soltgt
->soltgt_cnt
== 0) {
1416 VERIFY(soltgt
== NULL
|| TAILQ_EMPTY(&soltgt
->soltgt_q
));
1420 VERIFY(soltgt
->soltgt_cnt
!= 0);
1421 --soltgt
->soltgt_cnt
;
1422 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1423 VERIFY(ssrc
!= NULL
);
1424 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1425 *daddr
= *(&ssrc
->solsrc_saddr
);
1426 *ifp
= ssrc
->solsrc_ifp
;
1427 nd6_solsrc_free(ssrc
);
1432 static struct nd6_prproxy_soltgt
*
1433 nd6_soltgt_alloc(int how
)
1435 struct nd6_prproxy_soltgt
*soltgt
;
1437 soltgt
= zalloc_flags(soltgt_zone
, how
| Z_ZERO
);
1438 if (soltgt
!= NULL
) {
1439 TAILQ_INIT(&soltgt
->soltgt_q
);
1445 nd6_soltgt_free(struct nd6_prproxy_soltgt
*soltgt
)
1447 struct nd6_prproxy_solsrc
*ssrc
, *tssrc
;
1449 TAILQ_FOREACH_SAFE(ssrc
, &soltgt
->soltgt_q
, solsrc_tqe
, tssrc
) {
1450 VERIFY(soltgt
->soltgt_cnt
> 0);
1451 soltgt
->soltgt_cnt
--;
1452 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1453 nd6_solsrc_free(ssrc
);
1456 VERIFY(soltgt
->soltgt_cnt
== 0);
1457 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1459 zfree(soltgt_zone
, soltgt
);
1463 nd6_soltgt_prune(struct nd6_prproxy_soltgt
*soltgt
, u_int32_t max_ssrc
)
1465 while (soltgt
->soltgt_cnt
>= max_ssrc
) {
1466 struct nd6_prproxy_solsrc
*ssrc
;
1468 VERIFY(soltgt
->soltgt_cnt
!= 0);
1469 --soltgt
->soltgt_cnt
;
1470 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1471 VERIFY(ssrc
!= NULL
);
1472 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1473 nd6_solsrc_free(ssrc
);
1478 * Solicited target tree comparison function.
1480 * An ordered predicate is necessary; bcmp() is not documented to return
1481 * an indication of order, memcmp() is, and is an ISO C99 requirement.
1484 soltgt_cmp(const struct nd6_prproxy_soltgt
*a
,
1485 const struct nd6_prproxy_soltgt
*b
)
1487 return memcmp(&a
->soltgt_key
, &b
->soltgt_key
, sizeof(a
->soltgt_key
));