2 * Copyright (c) 2011-2012 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 MAUXF_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 (rt
== NULL
|| !(rt
->rt_flags
& RTF_UP
) ||
545 rt
->generation_id
!= route_generation
) {
549 rt
= ro6
->ro_rt
= NULL
;
552 /* Caller must have ensured this condition (not srcrt) */
553 VERIFY(IN6_ARE_ADDR_EQUAL(&ip6
->ip6_dst
,
554 &ro6
->ro_dst
.sin6_addr
));
556 rtalloc_scoped_ign((struct route
*)ro6
, RTF_PRCLONING
, ifscope
);
557 if ((rt
= ro6
->ro_rt
) == NULL
)
563 ours
= (rt
->rt_flags
& RTF_PROXY
) ? TRUE
: FALSE
;
568 m
->m_pkthdr
.aux_flags
|= MAUXF_PROXY_DST
;
574 * Called when a prefix transitions between on-link and off-link. Perform
575 * routing (RTF_PROXY) and interface (all-multicast) related operations on
576 * the affected prefixes.
579 nd6_prproxy_prelist_update(struct nd_prefix
*pr_cur
, struct nd_prefix
*pr_up
)
581 SLIST_HEAD(, nd6_prproxy_prelist
) up_head
;
582 SLIST_HEAD(, nd6_prproxy_prelist
) down_head
;
583 struct nd6_prproxy_prelist
*up
, *down
;
584 struct nd_prefix
*pr
;
585 struct in6_addr pr_addr
;
589 SLIST_INIT(&up_head
);
590 SLIST_INIT(&down_head
);
591 VERIFY(pr_cur
!= NULL
);
593 lck_mtx_assert(&proxy6_lock
, LCK_MTX_ASSERT_OWNED
);
596 * Upstream prefix. If caller did not specify one, search for one
597 * based on the information in current prefix. Caller is expected
598 * to have held an extra reference for the passed-in prefixes.
600 lck_mtx_lock(nd6_mutex
);
603 bcopy(&pr_cur
->ndpr_prefix
.sin6_addr
, &pr_addr
,
605 pr_len
= pr_cur
->ndpr_plen
;
608 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
610 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
611 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
612 pr
->ndpr_plen
!= pr_len
||
613 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
622 if ((pr_up
= pr
) == NULL
) {
623 lck_mtx_unlock(nd6_mutex
);
629 bcopy(&pr_up
->ndpr_prefix
.sin6_addr
, &pr_addr
,
631 pr_len
= pr_up
->ndpr_plen
;
633 NDPR_LOCK_ASSERT_HELD(pr_up
);
635 * Upstream prefix could be offlink by now; therefore we cannot
636 * assert that NDPRF_PRPROXY is set; however, we can insist that
637 * it must not be a scoped prefix.
639 VERIFY(!(pr_up
->ndpr_stateflags
& NDPRF_IFSCOPE
));
640 enable
= (pr_up
->ndpr_stateflags
& NDPRF_PRPROXY
);
643 up
= nd6_ndprl_alloc(M_WAITOK
);
645 lck_mtx_unlock(nd6_mutex
);
650 up
->ndprl_pr
= pr_up
;
651 SLIST_INSERT_HEAD(&up_head
, up
, ndprl_le
);
654 * Now build a list of matching (scoped) downstream prefixes on other
655 * interfaces which need to be enabled/disabled accordingly. Note that
656 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
658 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
660 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
661 !(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
) ||
662 pr
->ndpr_plen
!= pr_len
||
663 !in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
670 down
= nd6_ndprl_alloc(M_WAITOK
);
677 down
->ndprl_up
= pr_up
;
678 SLIST_INSERT_HEAD(&down_head
, down
, ndprl_le
);
680 lck_mtx_unlock(nd6_mutex
);
683 * Apply routing function on prefixes; callee will free resources.
685 nd6_prproxy_prelist_setroute(enable
,
686 (struct nd6_prproxy_prelist_head
*)&up_head
,
687 (struct nd6_prproxy_prelist_head
*)&down_head
);
690 VERIFY(SLIST_EMPTY(&up_head
));
691 VERIFY(SLIST_EMPTY(&down_head
));
695 * Given an interface address, determine whether or not the address
696 * is part of of a proxied prefix.
699 nd6_prproxy_ifaddr(struct in6_ifaddr
*ia
)
701 struct nd_prefix
*pr
;
702 struct in6_addr addr
, pr_mask
;
704 boolean_t proxied
= FALSE
;
706 lck_mtx_assert(nd6_mutex
, LCK_MTX_ASSERT_NOTOWNED
);
708 IFA_LOCK(&ia
->ia_ifa
);
709 bcopy(&ia
->ia_addr
.sin6_addr
, &addr
, sizeof (addr
));
710 bcopy(&ia
->ia_prefixmask
.sin6_addr
, &pr_mask
, sizeof (pr_mask
));
711 pr_len
= ia
->ia_plen
;
712 IFA_UNLOCK(&ia
->ia_ifa
);
714 lck_mtx_lock(nd6_mutex
);
715 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
717 if ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
718 (pr
->ndpr_stateflags
& NDPRF_PRPROXY
) &&
719 in6_are_prefix_equal(&pr
->ndpr_prefix
.sin6_addr
,
727 lck_mtx_unlock(nd6_mutex
);
733 * Perform automatic proxy function with NS output.
735 * If the target address matches a global prefix obtained from a router
736 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
737 * flag set, then we send solicitations for the target address to all other
738 * interfaces where a matching prefix is currently on-link, in addition to
739 * the original interface.
742 nd6_prproxy_ns_output(struct ifnet
*ifp
, struct in6_addr
*daddr
,
743 struct in6_addr
*taddr
, struct llinfo_nd6
*ln
)
745 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
746 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
747 struct nd_prefix
*pr
, *fwd
;
748 struct ifnet
*fwd_ifp
;
749 struct in6_addr pr_addr
;
752 SLIST_INIT(&ndprl_head
);
754 lck_mtx_lock(nd6_mutex
);
756 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
758 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
759 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
760 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
761 taddr
, &pr
->ndpr_mask
)) {
766 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
767 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
768 pr_len
= pr
->ndpr_plen
;
771 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
773 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
774 fwd
->ndpr_ifp
== ifp
||
775 fwd
->ndpr_plen
!= pr_len
||
776 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
782 fwd_ifp
= fwd
->ndpr_ifp
;
785 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
790 ndprl
->ndprl_pr
= fwd
;
791 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
793 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
798 lck_mtx_unlock(nd6_mutex
);
800 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
801 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
803 pr
= ndprl
->ndprl_pr
;
804 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
806 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
808 nd6_ndprl_free(ndprl
);
813 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
816 "%s%d: Sending cloned NS who has %s on %s%d\n",
817 fwd_ifp
->if_name
, fwd_ifp
->if_unit
,
818 ip6_sprintf(taddr
), ifp
->if_name
,
821 nd6_ns_output(fwd_ifp
, daddr
, taddr
, NULL
, 0);
827 nd6_ndprl_free(ndprl
);
829 VERIFY(SLIST_EMPTY(&ndprl_head
));
831 nd6_ns_output(ifp
, daddr
, taddr
, ln
, 0);
835 * Perform automatic proxy function with NS input.
837 * If the target address matches a global prefix obtained from a router
838 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
839 * flag set, then we send solicitations for the target address to all other
840 * interfaces where a matching prefix is currently on-link.
843 nd6_prproxy_ns_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
844 char *lladdr
, int lladdrlen
, struct in6_addr
*daddr
, struct in6_addr
*taddr
)
846 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
847 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
848 struct nd_prefix
*pr
, *fwd
;
849 struct ifnet
*fwd_ifp
;
850 struct in6_addr pr_addr
;
852 boolean_t solrec
= FALSE
;
854 SLIST_INIT(&ndprl_head
);
856 lck_mtx_lock(nd6_mutex
);
858 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
860 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
861 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
862 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
863 taddr
, &pr
->ndpr_mask
)) {
868 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
869 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
, sizeof (pr_addr
));
870 pr_len
= pr
->ndpr_plen
;
873 * If this is a NS for NUD/AR, record it so that we know
874 * how to forward the NA reply later on (if/when it arrives.)
875 * Give up if we fail to save the NS info.
877 if ((solrec
= !IN6_IS_ADDR_UNSPECIFIED(saddr
)) &&
878 !nd6_solsrc_enq(pr
, ifp
, saddr
, taddr
)) {
881 break; /* bail out */
886 for (fwd
= nd_prefix
.lh_first
; fwd
; fwd
= fwd
->ndpr_next
) {
888 if (!(fwd
->ndpr_stateflags
& NDPRF_ONLINK
) ||
889 fwd
->ndpr_ifp
== ifp
||
890 fwd
->ndpr_plen
!= pr_len
||
891 !in6_are_prefix_equal(&fwd
->ndpr_prefix
.sin6_addr
,
897 fwd_ifp
= fwd
->ndpr_ifp
;
900 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
905 ndprl
->ndprl_pr
= fwd
;
906 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
907 ndprl
->ndprl_sol
= solrec
;
909 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
914 lck_mtx_unlock(nd6_mutex
);
917 * If this is a recorded solicitation (NS for NUD/AR), create
918 * or update the neighbor cache entry for the soliciting node.
919 * Later on, when the NA reply arrives, we will need this cache
920 * entry in order to send the NA back to the original solicitor.
921 * Without a neighbor cache entry, we'd end up with an endless
922 * cycle of NS ping-pong between the us (the proxy) and the node
923 * which is soliciting for the address.
926 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
927 nd6_cache_lladdr(ifp
, saddr
, lladdr
, lladdrlen
,
928 ND_NEIGHBOR_SOLICIT
, 0);
931 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
932 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
934 pr
= ndprl
->ndprl_pr
;
935 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
937 if ((fwd_ifp
->if_eflags
& IFEF_IPV6_ND6ALT
) != 0) {
939 nd6_ndprl_free(ndprl
);
944 if (pr
->ndpr_stateflags
& NDPRF_ONLINK
) {
947 "%s%d: Forwarding NS (%s) from %s to %s who has %s "
948 "on %s%d\n", fwd_ifp
->if_name
, fwd_ifp
->if_unit
,
949 ndprl
->ndprl_sol
? "NUD/AR" : "DAD",
950 ip6_sprintf(saddr
), ip6_sprintf(daddr
),
951 ip6_sprintf(taddr
), ifp
->if_name
, ifp
->if_unit
));
953 nd6_ns_output(fwd_ifp
, ndprl
->ndprl_sol
? taddr
: NULL
,
954 taddr
, NULL
, !ndprl
->ndprl_sol
);
960 nd6_ndprl_free(ndprl
);
962 VERIFY(SLIST_EMPTY(&ndprl_head
));
966 * Perform automatic proxy function with NA input.
968 * If the target address matches a global prefix obtained from a router
969 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES flag
970 * set, then we send neighbor advertisements for the target address on all
971 * other interfaces where a matching prefix is currently on link.
974 nd6_prproxy_na_input(struct ifnet
*ifp
, struct in6_addr
*saddr
,
975 struct in6_addr
*daddr0
, struct in6_addr
*taddr
, int flags
)
977 SLIST_HEAD(, nd6_prproxy_prelist
) ndprl_head
;
978 struct nd6_prproxy_prelist
*ndprl
, *ndprl_tmp
;
979 struct nd_prefix
*pr
;
980 struct ifnet
*fwd_ifp
;
981 struct in6_addr daddr
;
983 SLIST_INIT(&ndprl_head
);
986 lck_mtx_lock(nd6_mutex
);
988 for (pr
= nd_prefix
.lh_first
; pr
; pr
= pr
->ndpr_next
) {
990 if (!(pr
->ndpr_stateflags
& NDPRF_ONLINK
) ||
991 !(pr
->ndpr_stateflags
& NDPRF_PRPROXY
) ||
992 !IN6_ARE_MASKED_ADDR_EQUAL(&pr
->ndpr_prefix
.sin6_addr
,
993 taddr
, &pr
->ndpr_mask
)) {
998 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1000 * If this is a NA for NUD, see if there is a record created
1001 * for the corresponding NS; upon success, we get back the
1002 * interface where the NS originally arrived on, as well as
1003 * the soliciting node's address. Give up if we can't find it.
1005 if (!IN6_IS_ADDR_MULTICAST(daddr0
)) {
1007 bzero(&daddr
, sizeof (daddr
));
1008 if (!nd6_solsrc_deq(pr
, taddr
, &daddr
, &fwd_ifp
)) {
1010 break; /* bail out */
1012 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
) && fwd_ifp
);
1015 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1017 break; /* bail out */
1019 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1020 ndprl
->ndprl_sol
= TRUE
;
1021 ndprl
->ndprl_sol_saddr
= *(&daddr
);
1023 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1025 struct nd_prefix
*fwd
;
1026 struct in6_addr pr_addr
;
1029 bcopy(&pr
->ndpr_prefix
.sin6_addr
, &pr_addr
,
1031 pr_len
= pr
->ndpr_plen
;
1034 for (fwd
= nd_prefix
.lh_first
; fwd
;
1035 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(
1041 &fwd
->ndpr_prefix
.sin6_addr
,
1042 &pr_addr
, pr_len
)) {
1047 fwd_ifp
= fwd
->ndpr_ifp
;
1050 ndprl
= nd6_ndprl_alloc(M_WAITOK
);
1055 ndprl
->ndprl_pr
= fwd
;
1056 ndprl
->ndprl_fwd_ifp
= fwd_ifp
;
1058 SLIST_INSERT_HEAD(&ndprl_head
, ndprl
, ndprl_le
);
1064 lck_mtx_unlock(nd6_mutex
);
1066 SLIST_FOREACH_SAFE(ndprl
, &ndprl_head
, ndprl_le
, ndprl_tmp
) {
1069 SLIST_REMOVE(&ndprl_head
, ndprl
, nd6_prproxy_prelist
, ndprl_le
);
1071 pr
= ndprl
->ndprl_pr
;
1072 fwd_ifp
= ndprl
->ndprl_fwd_ifp
;
1074 if (ndprl
->ndprl_sol
) {
1076 daddr
= *(&ndprl
->ndprl_sol_saddr
);
1077 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr
));
1078 send_na
= (in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1083 send_na
= ((pr
->ndpr_stateflags
& NDPRF_ONLINK
) &&
1084 in6_setscope(&daddr
, fwd_ifp
, NULL
) == 0);
1089 if (!ndprl
->ndprl_sol
) {
1091 "%s%d: Forwarding NA (DAD) from %s to %s "
1092 "tgt is %s on %s%d\n",
1093 fwd_ifp
->if_name
, fwd_ifp
->if_unit
,
1094 ip6_sprintf(saddr
), ip6_sprintf(&daddr
),
1095 ip6_sprintf(taddr
), ifp
->if_name
,
1099 "%s%d: Forwarding NA (NUD/AR) from %s to "
1100 "%s (was %s) tgt is %s on %s%d\n",
1101 fwd_ifp
->if_name
, fwd_ifp
->if_unit
,
1102 ip6_sprintf(saddr
), ip6_sprintf(&daddr
),
1103 ip6_sprintf(daddr0
), ip6_sprintf(taddr
),
1104 ifp
->if_name
, ifp
->if_unit
));
1107 nd6_na_output(fwd_ifp
, &daddr
, taddr
, flags
, 1, NULL
);
1113 nd6_ndprl_free(ndprl
);
1115 VERIFY(SLIST_EMPTY(&ndprl_head
));
1118 static struct nd6_prproxy_solsrc
*
1119 nd6_solsrc_alloc(int how
)
1121 struct nd6_prproxy_solsrc
*ssrc
;
1123 ssrc
= (how
== M_WAITOK
) ? zalloc(solsrc_zone
) :
1124 zalloc_noblock(solsrc_zone
);
1126 bzero(ssrc
, solsrc_size
);
1132 nd6_solsrc_free(struct nd6_prproxy_solsrc
*ssrc
)
1134 zfree(solsrc_zone
, ssrc
);
1138 nd6_prproxy_sols_purge(struct nd_prefix
*pr
, u_int64_t max_stgt
)
1140 struct nd6_prproxy_soltgt
*soltgt
, *tmp
;
1141 u_int64_t expire
= (max_stgt
> 0) ? net_uptime() : 0;
1143 NDPR_LOCK_ASSERT_HELD(pr
);
1145 /* Either trim all or those that have expired or are idle */
1146 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1147 &pr
->ndpr_prproxy_sols
, tmp
) {
1148 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1149 if (expire
== 0 || soltgt
->soltgt_expire
<= expire
||
1150 soltgt
->soltgt_cnt
== 0) {
1151 pr
->ndpr_prproxy_sols_cnt
--;
1152 RB_REMOVE(prproxy_sols_tree
,
1153 &pr
->ndpr_prproxy_sols
, soltgt
);
1154 nd6_soltgt_free(soltgt
);
1158 if (max_stgt
== 0 || pr
->ndpr_prproxy_sols_cnt
< max_stgt
) {
1159 VERIFY(max_stgt
!= 0 || (pr
->ndpr_prproxy_sols_cnt
== 0 &&
1160 RB_EMPTY(&pr
->ndpr_prproxy_sols
)));
1164 /* Brute force; mercilessly evict entries until we are under limit */
1165 RB_FOREACH_SAFE(soltgt
, prproxy_sols_tree
,
1166 &pr
->ndpr_prproxy_sols
, tmp
) {
1167 VERIFY(pr
->ndpr_prproxy_sols_cnt
> 0);
1168 pr
->ndpr_prproxy_sols_cnt
--;
1169 RB_REMOVE(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1170 nd6_soltgt_free(soltgt
);
1171 if (pr
->ndpr_prproxy_sols_cnt
< max_stgt
)
1177 * Purges all solicitation records on a given prefix.
1178 * Caller is responsible for holding prefix lock.
1181 nd6_prproxy_sols_reap(struct nd_prefix
*pr
)
1183 nd6_prproxy_sols_purge(pr
, 0);
1187 * Purges expired or idle solicitation records on a given prefix.
1188 * Caller is responsible for holding prefix lock.
1191 nd6_prproxy_sols_prune(struct nd_prefix
*pr
, u_int32_t max_stgt
)
1193 nd6_prproxy_sols_purge(pr
, max_stgt
);
1197 * Enqueue a soliciation record in the target record of a prefix.
1200 nd6_solsrc_enq(struct nd_prefix
*pr
, struct ifnet
*ifp
,
1201 struct in6_addr
*saddr
, struct in6_addr
*taddr
)
1203 struct nd6_prproxy_soltgt find
, *soltgt
;
1204 struct nd6_prproxy_solsrc
*ssrc
;
1205 u_int32_t max_stgt
= nd6_max_tgt_sols
;
1206 u_int32_t max_ssrc
= nd6_max_src_sols
;
1208 NDPR_LOCK_ASSERT_HELD(pr
);
1209 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1210 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
|NDPRF_PRPROXY
)) ==
1211 (NDPRF_ONLINK
|NDPRF_PRPROXY
));
1212 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr
));
1214 ssrc
= nd6_solsrc_alloc(M_WAITOK
);
1218 ssrc
->solsrc_saddr
= *saddr
;
1219 ssrc
->solsrc_ifp
= ifp
;
1221 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1223 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1224 if (soltgt
== NULL
) {
1225 if (max_stgt
!= 0 && pr
->ndpr_prproxy_sols_cnt
>= max_stgt
) {
1226 VERIFY(!RB_EMPTY(&pr
->ndpr_prproxy_sols
));
1227 nd6_prproxy_sols_prune(pr
, max_stgt
);
1228 VERIFY(pr
->ndpr_prproxy_sols_cnt
< max_stgt
);
1231 soltgt
= nd6_soltgt_alloc(M_WAITOK
);
1232 if (soltgt
== NULL
) {
1233 nd6_solsrc_free(ssrc
);
1237 soltgt
->soltgt_key
.taddr
= *taddr
;
1238 VERIFY(soltgt
->soltgt_cnt
== 0);
1239 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1241 pr
->ndpr_prproxy_sols_cnt
++;
1242 VERIFY(pr
->ndpr_prproxy_sols_cnt
!= 0);
1243 RB_INSERT(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, soltgt
);
1246 if (max_ssrc
!= 0 && soltgt
->soltgt_cnt
>= max_ssrc
) {
1247 VERIFY(!TAILQ_EMPTY(&soltgt
->soltgt_q
));
1248 nd6_soltgt_prune(soltgt
, max_ssrc
);
1249 VERIFY(soltgt
->soltgt_cnt
< max_ssrc
);
1252 soltgt
->soltgt_cnt
++;
1253 VERIFY(soltgt
->soltgt_cnt
!= 0);
1254 TAILQ_INSERT_TAIL(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1255 if (soltgt
->soltgt_cnt
== 1)
1256 soltgt
->soltgt_expire
= net_uptime() + ND6_TGT_SOLS_EXPIRE
;
1262 * Dequeue a solicitation record from a target record of a prefix.
1265 nd6_solsrc_deq(struct nd_prefix
*pr
, struct in6_addr
*taddr
,
1266 struct in6_addr
*daddr
, struct ifnet
**ifp
)
1268 struct nd6_prproxy_soltgt find
, *soltgt
;
1269 struct nd6_prproxy_solsrc
*ssrc
;
1271 NDPR_LOCK_ASSERT_HELD(pr
);
1272 VERIFY(!(pr
->ndpr_stateflags
& NDPRF_IFSCOPE
));
1273 VERIFY((pr
->ndpr_stateflags
& (NDPRF_ONLINK
|NDPRF_PRPROXY
)) ==
1274 (NDPRF_ONLINK
|NDPRF_PRPROXY
));
1276 bzero(daddr
, sizeof (*daddr
));
1279 find
.soltgt_key
.taddr
= *taddr
; /* search key */
1281 soltgt
= RB_FIND(prproxy_sols_tree
, &pr
->ndpr_prproxy_sols
, &find
);
1282 if (soltgt
== NULL
|| soltgt
->soltgt_cnt
== 0) {
1283 VERIFY(soltgt
== NULL
|| TAILQ_EMPTY(&soltgt
->soltgt_q
));
1287 VERIFY(soltgt
->soltgt_cnt
!= 0);
1288 --soltgt
->soltgt_cnt
;
1289 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1290 VERIFY(ssrc
!= NULL
);
1291 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1292 *daddr
= *(&ssrc
->solsrc_saddr
);
1293 *ifp
= ssrc
->solsrc_ifp
;
1294 nd6_solsrc_free(ssrc
);
1299 static struct nd6_prproxy_soltgt
*
1300 nd6_soltgt_alloc(int how
)
1302 struct nd6_prproxy_soltgt
*soltgt
;
1304 soltgt
= (how
== M_WAITOK
) ? zalloc(soltgt_zone
) :
1305 zalloc_noblock(soltgt_zone
);
1306 if (soltgt
!= NULL
) {
1307 bzero(soltgt
, soltgt_size
);
1308 TAILQ_INIT(&soltgt
->soltgt_q
);
1314 nd6_soltgt_free(struct nd6_prproxy_soltgt
*soltgt
)
1316 struct nd6_prproxy_solsrc
*ssrc
, *tssrc
;
1318 TAILQ_FOREACH_SAFE(ssrc
, &soltgt
->soltgt_q
, solsrc_tqe
, tssrc
) {
1319 VERIFY(soltgt
->soltgt_cnt
> 0);
1320 soltgt
->soltgt_cnt
--;
1321 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1322 nd6_solsrc_free(ssrc
);
1325 VERIFY(soltgt
->soltgt_cnt
== 0);
1326 VERIFY(TAILQ_EMPTY(&soltgt
->soltgt_q
));
1328 zfree(soltgt_zone
, soltgt
);
1332 nd6_soltgt_prune(struct nd6_prproxy_soltgt
*soltgt
, u_int32_t max_ssrc
)
1334 while (soltgt
->soltgt_cnt
>= max_ssrc
) {
1335 struct nd6_prproxy_solsrc
*ssrc
;
1337 VERIFY(soltgt
->soltgt_cnt
!= 0);
1338 --soltgt
->soltgt_cnt
;
1339 ssrc
= TAILQ_FIRST(&soltgt
->soltgt_q
);
1340 VERIFY(ssrc
!= NULL
);
1341 TAILQ_REMOVE(&soltgt
->soltgt_q
, ssrc
, solsrc_tqe
);
1342 nd6_solsrc_free(ssrc
);
1347 * Solicited target tree comparison function.
1349 * An ordered predicate is necessary; bcmp() is not documented to return
1350 * an indication of order, memcmp() is, and is an ISO C99 requirement.
1353 soltgt_cmp(const struct nd6_prproxy_soltgt
*a
,
1354 const struct nd6_prproxy_soltgt
*b
)
1356 return (memcmp(&a
->soltgt_key
, &b
->soltgt_key
, sizeof (a
->soltgt_key
)));