1/*
2 * Copyright (c) 2011-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * Prefix-based Neighbor Discovery Proxy
31 *
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.
38 *
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.
44 *
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.
50 *
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.
56 *
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.
67 *
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.
71 *
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.)
75 *
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.
80 */
81
82#include <sys/param.h>
83#include <sys/systm.h>
84#include <sys/malloc.h>
85#include <sys/mbuf.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>
91
92#include <kern/queue.h>
93#include <kern/zalloc.h>
94
95#include <net/if.h>
96#include <net/if_var.h>
97#include <net/if_types.h>
98#include <net/route.h>
99
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>
108
109struct 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 */
116};
117
118/*
119 * Soliciting node (source) record.
120 */
121struct 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 */
125};
126
127/*
128 * Solicited node (target) record.
129 */
130struct 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 */
134 } soltgt_key;
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;
138};
139
140SLIST_HEAD(nd6_prproxy_prelist_head, nd6_prproxy_prelist);
141
142static void nd6_prproxy_prelist_setroute(boolean_t enable,
143 struct nd6_prproxy_prelist_head *, struct nd6_prproxy_prelist_head *);
144static struct nd6_prproxy_prelist *nd6_ndprl_alloc(zalloc_flags_t);
145static void nd6_ndprl_free(struct nd6_prproxy_prelist *);
146static struct nd6_prproxy_solsrc *nd6_solsrc_alloc(int);
147static void nd6_solsrc_free(struct nd6_prproxy_solsrc *);
148static boolean_t nd6_solsrc_enq(struct nd_prefix *, struct ifnet *,
149 struct in6_addr *, struct in6_addr *);
150static boolean_t nd6_solsrc_deq(struct nd_prefix *, struct in6_addr *,
151 struct in6_addr *, struct ifnet **);
152static struct nd6_prproxy_soltgt *nd6_soltgt_alloc(int);
153static void nd6_soltgt_free(struct nd6_prproxy_soltgt *);
154static void nd6_soltgt_prune(struct nd6_prproxy_soltgt *, u_int32_t);
155static __inline int soltgt_cmp(const struct nd6_prproxy_soltgt *,
156 const struct nd6_prproxy_soltgt *);
157static void nd6_prproxy_sols_purge(struct nd_prefix *, u_int64_t);
158
159RB_PROTOTYPE_SC_PREV(__private_extern__, prproxy_sols_tree, nd6_prproxy_soltgt,
160 soltgt_link, soltgt_cmp);
161
162/*
163 * Time (in seconds) before a target record expires (is idle).
164 */
165#define ND6_TGT_SOLS_EXPIRE 5
166
167/*
168 * Maximum number of queued soliciting (source) records per target.
169 */
170#define ND6_MAX_SRC_SOLS_DEFAULT 4
171
172/*
173 * Maximum number of queued solicited (target) records per prefix.
174 */
175#define ND6_MAX_TGT_SOLS_DEFAULT 8
176
177static u_int32_t nd6_max_tgt_sols = ND6_MAX_TGT_SOLS_DEFAULT;
178static u_int32_t nd6_max_src_sols = ND6_MAX_SRC_SOLS_DEFAULT;
179
180static KALLOC_TYPE_DEFINE(ndprl_zone,
181 struct nd6_prproxy_prelist, NET_KT_DEFAULT); /* nd6_prproxy_prelist zone */
182
183static KALLOC_TYPE_DEFINE(solsrc_zone,
184 struct nd6_prproxy_solsrc, NET_KT_DEFAULT); /* nd6_prproxy_solsrc zone */
185
186static KALLOC_TYPE_DEFINE(soltgt_zone,
187 struct nd6_prproxy_soltgt, NET_KT_DEFAULT); /* nd6_prproxy_soltgt zone */
188
189/* The following is protected by ndpr_lock */
190RB_GENERATE_PREV(prproxy_sols_tree, nd6_prproxy_soltgt,
191 soltgt_link, soltgt_cmp);
192
193/* The following is protected by proxy6_lock (for updates) */
194u_int32_t nd6_prproxy;
195
196SYSCTL_DECL(_net_inet6_icmp6);
197
198SYSCTL_UINT(_net_inet6_icmp6, OID_AUTO, nd6_maxsolstgt,
199 CTLFLAG_RW | CTLFLAG_LOCKED, &nd6_max_tgt_sols, ND6_MAX_TGT_SOLS_DEFAULT,
200 "maximum number of outstanding solicited targets per prefix");
201
202SYSCTL_UINT(_net_inet6_icmp6, OID_AUTO, nd6_maxproxiedsol,
203 CTLFLAG_RW | CTLFLAG_LOCKED, &nd6_max_src_sols, ND6_MAX_SRC_SOLS_DEFAULT,
204 "maximum number of outstanding solicitations per target");
205
206SYSCTL_UINT(_net_inet6_icmp6, OID_AUTO, prproxy_cnt,
207 CTLFLAG_RD | CTLFLAG_LOCKED, &nd6_prproxy, 0,
208 "total number of proxied prefixes");
209
210static struct nd6_prproxy_prelist *
211nd6_ndprl_alloc(zalloc_flags_t how)
212{
213 return zalloc_flags(ndprl_zone, how | Z_ZERO);
214}
215
216static void
217nd6_ndprl_free(struct nd6_prproxy_prelist *ndprl)
218{
219 zfree(ndprl_zone, ndprl);
220}
221
222/*
223 * Apply routing function on the affected upstream and downstream prefixes,
224 * i.e. either set or clear RTF_PROXY on the cloning prefix route; all route
225 * entries that were cloned off these prefixes will be blown away. Caller
226 * must have acquired proxy6_lock and must not be holding nd6_mutex.
227 */
228static void
229nd6_prproxy_prelist_setroute(boolean_t enable,
230 struct nd6_prproxy_prelist_head *up_head,
231 struct nd6_prproxy_prelist_head *down_head)
232{
233 struct nd6_prproxy_prelist *up, *down, *ndprl_tmp;
234 struct nd_prefix *pr;
235
236 LCK_MTX_ASSERT(&proxy6_lock, LCK_MTX_ASSERT_OWNED);
237 LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED);
238
239 SLIST_FOREACH_SAFE(up, up_head, ndprl_le, ndprl_tmp) {
240 struct rtentry *rt;
241 boolean_t prproxy, set_allmulti = FALSE;
242 int allmulti_sw = FALSE;
243 struct ifnet *ifp = NULL;
244
245 SLIST_REMOVE(up_head, up, nd6_prproxy_prelist, ndprl_le);
246 pr = up->ndprl_pr;
247 VERIFY(up->ndprl_up == NULL);
248
249 NDPR_LOCK(pr);
250 ifp = pr->ndpr_ifp;
251 prproxy = (pr->ndpr_stateflags & NDPRF_PRPROXY);
252 VERIFY(!prproxy || ((pr->ndpr_stateflags & NDPRF_ONLINK) &&
253 !(pr->ndpr_stateflags & NDPRF_IFSCOPE)));
254
255 nd6_prproxy_sols_reap(pr);
256 VERIFY(pr->ndpr_prproxy_sols_cnt == 0);
257 VERIFY(RB_EMPTY(&pr->ndpr_prproxy_sols));
258
259 if (enable && pr->ndpr_allmulti_cnt == 0) {
260 nd6_prproxy++;
261 pr->ndpr_allmulti_cnt++;
262 set_allmulti = TRUE;
263 allmulti_sw = TRUE;
264 } else if (!enable && pr->ndpr_allmulti_cnt > 0) {
265 nd6_prproxy--;
266 pr->ndpr_allmulti_cnt--;
267 set_allmulti = TRUE;
268 allmulti_sw = FALSE;
269 }
270
271 if ((rt = pr->ndpr_rt) != NULL) {
272 if ((enable && prproxy) || (!enable && !prproxy)) {
273 RT_ADDREF(rt);
274 } else {
275 rt = NULL;
276 }
277 NDPR_UNLOCK(pr);
278 } else {
279 NDPR_UNLOCK(pr);
280 }
281
282 /* Call the following ioctl after releasing NDPR lock */
283 if (set_allmulti && ifp != NULL) {
284 if_allmulti(ifp, allmulti_sw);
285 }
286
287
288 NDPR_REMREF(pr);
289 if (rt != NULL) {
290 rt_set_proxy(rt, enable);
291 rtfree(rt);
292 }
293 nd6_ndprl_free(ndprl: up);
294 }
295
296 SLIST_FOREACH_SAFE(down, down_head, ndprl_le, ndprl_tmp) {
297 struct nd_prefix *pr_up;
298 struct rtentry *rt;
299 boolean_t prproxy, set_allmulti = FALSE;
300 int allmulti_sw = FALSE;
301 struct ifnet *ifp = NULL;
302
303 SLIST_REMOVE(down_head, down, nd6_prproxy_prelist, ndprl_le);
304 pr = down->ndprl_pr;
305 pr_up = down->ndprl_up;
306 VERIFY(pr_up != NULL);
307
308 NDPR_LOCK(pr_up);
309 ifp = pr->ndpr_ifp;
310 prproxy = (pr_up->ndpr_stateflags & NDPRF_PRPROXY);
311 VERIFY(!prproxy || ((pr_up->ndpr_stateflags & NDPRF_ONLINK) &&
312 !(pr_up->ndpr_stateflags & NDPRF_IFSCOPE)));
313 NDPR_UNLOCK(pr_up);
314
315 NDPR_LOCK(pr);
316 if (enable && pr->ndpr_allmulti_cnt == 0) {
317 pr->ndpr_allmulti_cnt++;
318 set_allmulti = TRUE;
319 allmulti_sw = TRUE;
320 } else if (!enable && pr->ndpr_allmulti_cnt > 0) {
321 pr->ndpr_allmulti_cnt--;
322 set_allmulti = TRUE;
323 allmulti_sw = FALSE;
324 }
325
326 if ((rt = pr->ndpr_rt) != NULL) {
327 if ((enable && prproxy) || (!enable && !prproxy)) {
328 RT_ADDREF(rt);
329 } else {
330 rt = NULL;
331 }
332 NDPR_UNLOCK(pr);
333 } else {
334 NDPR_UNLOCK(pr);
335 }
336 if (set_allmulti && ifp != NULL) {
337 if_allmulti(ifp, allmulti_sw);
338 }
339
340 NDPR_REMREF(pr);
341 NDPR_REMREF(pr_up);
342 if (rt != NULL) {
343 rt_set_proxy(rt, enable);
344 rtfree(rt);
345 }
346 nd6_ndprl_free(ndprl: down);
347 }
348}
349
350/*
351 * Enable/disable prefix proxying on an interface; typically called
352 * as part of handling SIOCSIFINFO_FLAGS[SETROUTERMODE_IN6]
353 */
354int
355nd6_if_prproxy(struct ifnet *ifp, boolean_t enable)
356{
357 SLIST_HEAD(, nd6_prproxy_prelist) up_head;
358 SLIST_HEAD(, nd6_prproxy_prelist) down_head;
359 struct nd6_prproxy_prelist *up, *down;
360 struct nd_prefix *pr;
361
362 /* Can't be enabled if we are an advertising router on the interface */
363 ifnet_lock_shared(ifp);
364 if (enable && (ifp->if_ipv6_router_mode == IPV6_ROUTER_MODE_EXCLUSIVE)) {
365 ifnet_lock_done(ifp);
366 return EBUSY;
367 }
368 ifnet_lock_done(ifp);
369
370 SLIST_INIT(&up_head);
371 SLIST_INIT(&down_head);
372
373 /*
374 * Serialize the clearing/setting of NDPRF_PRPROXY.
375 */
376 lck_mtx_lock(lck: &proxy6_lock);
377
378 /*
379 * First build a list of upstream prefixes on this interface for
380 * which we need to enable/disable prefix proxy functionality.
381 */
382 lck_mtx_lock(nd6_mutex);
383 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
384 NDPR_LOCK(pr);
385 if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
386 (!enable && !(pr->ndpr_stateflags & NDPRF_PRPROXY)) ||
387 (enable && (pr->ndpr_stateflags & NDPRF_PRPROXY)) ||
388 (pr->ndpr_stateflags & NDPRF_IFSCOPE) ||
389 pr->ndpr_ifp != ifp) {
390 NDPR_UNLOCK(pr);
391 continue;
392 }
393
394 /*
395 * At present, in order for the prefix to be eligible
396 * as a proxying/proxied prefix, we require that the
397 * prefix route entry be marked as a cloning route with
398 * RTF_PROXY; i.e. nd6_need_cache() needs to return
399 * true for the interface type.
400 */
401 if (enable && (pr->ndpr_stateflags & NDPRF_ONLINK) &&
402 nd6_need_cache(ifp)) {
403 pr->ndpr_stateflags |= NDPRF_PRPROXY;
404 NDPR_ADDREF(pr);
405 NDPR_UNLOCK(pr);
406 } else if (!enable) {
407 pr->ndpr_stateflags &= ~NDPRF_PRPROXY;
408 NDPR_ADDREF(pr);
409 NDPR_UNLOCK(pr);
410 } else {
411 NDPR_UNLOCK(pr);
412 pr = NULL; /* don't go further */
413 }
414
415 if (pr == NULL) {
416 break;
417 }
418
419 up = nd6_ndprl_alloc(how: Z_WAITOK);
420 if (up == NULL) {
421 NDPR_REMREF(pr);
422 continue;
423 }
424
425 up->ndprl_pr = pr; /* keep reference from above */
426 SLIST_INSERT_HEAD(&up_head, up, ndprl_le);
427 }
428
429 /*
430 * Now build a list of matching (scoped) downstream prefixes on other
431 * interfaces which need to be enabled/disabled accordingly. Note that
432 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
433 */
434 SLIST_FOREACH(up, &up_head, ndprl_le) {
435 struct nd_prefix *fwd;
436 struct in6_addr pr_addr;
437 uint32_t pr_ifscope;
438 u_char pr_len;
439
440 pr = up->ndprl_pr;
441
442 NDPR_LOCK(pr);
443 bcopy(src: &pr->ndpr_prefix.sin6_addr, dst: &pr_addr, n: sizeof(pr_addr));
444 pr_len = pr->ndpr_plen;
445 pr_ifscope = pr->ndpr_prefix.sin6_scope_id;
446 NDPR_UNLOCK(pr);
447
448 for (fwd = nd_prefix.lh_first; fwd; fwd = fwd->ndpr_next) {
449 NDPR_LOCK(fwd);
450 if (!(fwd->ndpr_stateflags & NDPRF_ONLINK) ||
451 !(fwd->ndpr_stateflags & NDPRF_IFSCOPE) ||
452 fwd->ndpr_plen != pr_len ||
453 !in6_are_prefix_equal(p1: &fwd->ndpr_prefix.sin6_addr, ifscope1: fwd->ndpr_prefix.sin6_scope_id,
454 p2: &pr_addr, ifscope2: pr_ifscope, len: pr_len)) {
455 NDPR_UNLOCK(fwd);
456 continue;
457 }
458 NDPR_UNLOCK(fwd);
459
460 down = nd6_ndprl_alloc(how: Z_WAITOK);
461 if (down == NULL) {
462 continue;
463 }
464
465 NDPR_ADDREF(fwd);
466 down->ndprl_pr = fwd;
467 NDPR_ADDREF(pr);
468 down->ndprl_up = pr;
469 SLIST_INSERT_HEAD(&down_head, down, ndprl_le);
470 }
471 }
472 lck_mtx_unlock(nd6_mutex);
473
474 /*
475 * Apply routing function on prefixes; callee will free resources.
476 */
477 nd6_prproxy_prelist_setroute(enable,
478 up_head: (struct nd6_prproxy_prelist_head *)&up_head,
479 down_head: (struct nd6_prproxy_prelist_head *)&down_head);
480
481 VERIFY(SLIST_EMPTY(&up_head));
482 VERIFY(SLIST_EMPTY(&down_head));
483
484 lck_mtx_unlock(lck: &proxy6_lock);
485
486 return 0;
487}
488
489/*
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).
493 */
494boolean_t
495nd6_prproxy_isours(struct mbuf *m, struct ip6_hdr *ip6, struct route_in6 *ro6,
496 unsigned int ifscope)
497{
498 struct rtentry *rt;
499 boolean_t ours = FALSE;
500
501 if (ip6->ip6_hlim != IPV6_MAXHLIM || ip6->ip6_nxt != IPPROTO_ICMPV6) {
502 goto done;
503 }
504
505 if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst) ||
506 IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst)) {
507 VERIFY(ro6 == NULL);
508 ours = TRUE;
509 goto done;
510 } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
511 goto done;
512 }
513
514 if (ro6 == NULL) {
515 goto done;
516 }
517
518 if ((rt = ro6->ro_rt) != NULL) {
519 RT_LOCK(rt);
520 }
521
522 if (ROUTE_UNUSABLE(ro6)) {
523 if (rt != NULL) {
524 RT_UNLOCK(rt);
525 }
526
527 ROUTE_RELEASE(ro6);
528
529 /* Caller must have ensured this condition (not srcrt) */
530 VERIFY(in6_are_addr_equal_scoped(&ip6->ip6_dst,
531 &ro6->ro_dst.sin6_addr, ip6_input_getdstifscope(m), ro6->ro_dst.sin6_scope_id));
532
533 rtalloc_scoped_ign((struct route *)ro6, RTF_PRCLONING, ifscope);
534 if ((rt = ro6->ro_rt) == NULL) {
535 goto done;
536 }
537
538 RT_LOCK(rt);
539 }
540
541 ours = (rt->rt_flags & RTF_PROXY) ? TRUE : FALSE;
542 RT_UNLOCK(rt);
543
544done:
545 if (ours) {
546 m->m_pkthdr.pkt_flags |= PKTF_PROXY_DST;
547 }
548
549 return ours;
550}
551
552/*
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.
556 */
557void
558nd6_proxy_find_fwdroute(struct ifnet *ifp, struct route_in6 *ro6)
559{
560 struct in6_addr *dst6 = &ro6->ro_dst.sin6_addr;
561 uint32_t dst_ifscope = ro6->ro_dst.sin6_scope_id;
562 struct ifnet *fwd_ifp = NULL;
563 struct nd_prefix *pr;
564 struct rtentry *rt;
565
566 if ((rt = ro6->ro_rt) != NULL) {
567 RT_LOCK(rt);
568 if (!(rt->rt_flags & RTF_PROXY) || rt->rt_ifp == ifp) {
569 nd6log2(debug, "%s: found incorrect prefix "
570 "proxy route for dst %s on %s\n", if_name(ifp),
571 ip6_sprintf(dst6),
572 if_name(rt->rt_ifp));
573 RT_UNLOCK(rt);
574 /* look it up below */
575 } else {
576 RT_UNLOCK(rt);
577 /*
578 * The route is already marked with RTF_PRPROXY and
579 * it isn't pointing back to the inbound interface;
580 * optimistically return (see notes below).
581 */
582 return;
583 }
584 }
585
586 /*
587 * Find out where we should forward this packet to, by searching
588 * for another interface that is proxying for the prefix. Our
589 * current implementation assumes that the proxied prefix is shared
590 * to no more than one downstream interfaces (typically a bridge
591 * interface).
592 */
593 lck_mtx_lock(nd6_mutex);
594 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
595 struct in6_addr pr_addr;
596 struct nd_prefix *fwd;
597 uint32_t pr_ifscope = pr->ndpr_prefix.sin6_scope_id;
598
599 u_char pr_len;
600
601 NDPR_LOCK(pr);
602 if (!(pr->ndpr_stateflags & NDPRF_ONLINK) ||
603 !(pr->ndpr_stateflags & NDPRF_PRPROXY) ||
604 !in6_are_masked_addr_scope_equal(&pr->ndpr_prefix.sin6_addr, pr_ifscope,
605 dst6, dst_ifscope, &pr->ndpr_mask)) {
606 NDPR_UNLOCK(pr);
607 continue;
608 }
609
610 VERIFY(!(pr->ndpr_stateflags & NDPRF_IFSCOPE));
611 bcopy(src: &pr->ndpr_prefix.sin6_addr, dst: &pr_addr, n: sizeof(pr_addr));
612 pr_len = pr->ndpr_plen;
613 NDPR_UNLOCK(pr);
614
615 for (fwd = nd_prefix.lh_first; fwd; fwd = fwd->ndpr_next) {
616 NDPR_LOCK(fwd);
617 if (!(fwd->ndpr_stateflags & NDPRF_ONLINK) ||
618 fwd->ndpr_ifp == ifp ||
619 fwd->ndpr_plen != pr_len ||
620 !in6_are_prefix_equal(p1: &fwd->ndpr_prefix.sin6_addr, ifscope1: fwd->ndpr_prefix.sin6_scope_id,
621 p2: &pr_addr, ifscope2: pr_ifscope, len: pr_len)) {
622 NDPR_UNLOCK(fwd);
623 continue;
624 }
625
626 fwd_ifp = fwd->ndpr_ifp;
627 NDPR_UNLOCK(fwd);
628 break;
629 }
630 break;
631 }
632 lck_mtx_unlock(nd6_mutex);
633
634 lck_mtx_lock(rnh_lock);
635 ROUTE_RELEASE_LOCKED(ro6);
636
637 /*
638 * Lookup a forwarding route; delete the route if it's incorrect,
639 * or return to caller if the correct one got created prior to
640 * our acquiring the rnh_lock.
641 */
642 if ((rt = rtalloc1_scoped_locked(SA(&ro6->ro_dst), 0,
643 RTF_CLONING | RTF_PRCLONING, IFSCOPE_NONE)) != NULL) {
644 RT_LOCK(rt);
645 if (rt->rt_ifp != fwd_ifp || !(rt->rt_flags & RTF_PROXY)) {
646 rt->rt_flags |= RTF_CONDEMNED;
647 RT_UNLOCK(rt);
648 (void) rtrequest_locked(RTM_DELETE, rt_key(rt),
649 rt->rt_gateway, rt_mask(rt), rt->rt_flags, NULL);
650 rtfree_locked(rt);
651 rt = NULL;
652 } else {
653 nd6log2(debug, "%s: found prefix proxy route "
654 "for dst %s\n", if_name(rt->rt_ifp),
655 ip6_sprintf(dst6));
656 RT_UNLOCK(rt);
657 ro6->ro_rt = rt; /* refcnt held by rtalloc1 */
658 lck_mtx_unlock(rnh_lock);
659 return;
660 }
661 }
662 VERIFY(rt == NULL && ro6->ro_rt == NULL);
663
664 /*
665 * Clone a route from the correct parent prefix route and return it.
666 */
667 if (fwd_ifp != NULL && (rt = rtalloc1_scoped_locked(SA(&ro6->ro_dst), 1,
668 RTF_PRCLONING, fwd_ifp->if_index)) != NULL) {
669 RT_LOCK(rt);
670 if (!(rt->rt_flags & RTF_PROXY)) {
671 RT_UNLOCK(rt);
672 rtfree_locked(rt);
673 rt = NULL;
674 } else {
675 nd6log2(debug, "%s: allocated prefix proxy "
676 "route for dst %s\n", if_name(rt->rt_ifp),
677 ip6_sprintf(dst6));
678 RT_UNLOCK(rt);
679 ro6->ro_rt = rt; /* refcnt held by rtalloc1 */
680 }
681 }
682 VERIFY(rt != NULL || ro6->ro_rt == NULL);
683
684 if (fwd_ifp == NULL || rt == NULL) {
685 nd6log2(error, "%s: failed to find forwarding prefix "
686 "proxy entry for dst %s\n", if_name(ifp),
687 ip6_sprintf(dst6));
688 }
689 lck_mtx_unlock(rnh_lock);
690}
691
692/*
693 * Called when a prefix transitions between on-link and off-link. Perform
694 * routing (RTF_PROXY) and interface (all-multicast) related operations on
695 * the affected prefixes.
696 */
697void
698nd6_prproxy_prelist_update(struct nd_prefix *pr_cur, struct nd_prefix *pr_up)
699{
700 SLIST_HEAD(, nd6_prproxy_prelist) up_head;
701 SLIST_HEAD(, nd6_prproxy_prelist) down_head;
702 struct nd6_prproxy_prelist *up, *down;
703 struct nd_prefix *pr;
704 struct in6_addr pr_addr;
705 boolean_t enable;
706 u_char pr_len;
707 uint32_t pr_ifscope;
708
709 SLIST_INIT(&up_head);
710 SLIST_INIT(&down_head);
711 VERIFY(pr_cur != NULL);
712
713 LCK_MTX_ASSERT(&proxy6_lock, LCK_MTX_ASSERT_OWNED);
714
715 /*
716 * Upstream prefix. If caller did not specify one, search for one
717 * based on the information in current prefix. Caller is expected
718 * to have held an extra reference for the passed-in prefixes.
719 */
720 lck_mtx_lock(nd6_mutex);
721 if (pr_up == NULL) {
722 NDPR_LOCK(pr_cur);
723 bcopy(src: &pr_cur->ndpr_prefix.sin6_addr, dst: &pr_addr,
724 n: sizeof(pr_addr));
725 pr_len = pr_cur->ndpr_plen;
726 pr_ifscope = pr_cur->ndpr_prefix.sin6_scope_id;
727 NDPR_UNLOCK(pr_cur);
728
729 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
730 NDPR_LOCK(pr);
731 if (!(pr->ndpr_stateflags & NDPRF_ONLINK) ||
732 !(pr->ndpr_stateflags & NDPRF_PRPROXY) ||
733 pr->ndpr_plen != pr_len ||
734 !in6_are_prefix_equal(p1: &pr->ndpr_prefix.sin6_addr, ifscope1: pr->ndpr_prefix.sin6_scope_id,
735 p2: &pr_addr, ifscope2: pr_ifscope, len: pr_len)) {
736 NDPR_UNLOCK(pr);
737 continue;
738 }
739 NDPR_UNLOCK(pr);
740 break;
741 }
742
743 if ((pr_up = pr) == NULL) {
744 lck_mtx_unlock(nd6_mutex);
745 goto done;
746 }
747 NDPR_LOCK(pr_up);
748 } else {
749 NDPR_LOCK(pr_up);
750 bcopy(src: &pr_up->ndpr_prefix.sin6_addr, dst: &pr_addr,
751 n: sizeof(pr_addr));
752 pr_ifscope = pr_up->ndpr_prefix.sin6_scope_id;
753 pr_len = pr_up->ndpr_plen;
754 }
755 NDPR_LOCK_ASSERT_HELD(pr_up);
756 /*
757 * Upstream prefix could be offlink by now; therefore we cannot
758 * assert that NDPRF_PRPROXY is set; however, we can insist that
759 * it must not be a scoped prefix.
760 */
761 VERIFY(!(pr_up->ndpr_stateflags & NDPRF_IFSCOPE));
762 enable = (pr_up->ndpr_stateflags & NDPRF_PRPROXY);
763 NDPR_UNLOCK(pr_up);
764
765 up = nd6_ndprl_alloc(how: Z_WAITOK);
766 if (up == NULL) {
767 lck_mtx_unlock(nd6_mutex);
768 goto done;
769 }
770
771 NDPR_ADDREF(pr_up);
772 up->ndprl_pr = pr_up;
773 SLIST_INSERT_HEAD(&up_head, up, ndprl_le);
774
775 /*
776 * Now build a list of matching (scoped) downstream prefixes on other
777 * interfaces which need to be enabled/disabled accordingly. Note that
778 * the NDPRF_PRPROXY is never set/cleared on the downstream prefixes.
779 */
780 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
781 NDPR_LOCK(pr);
782 if (!(pr->ndpr_stateflags & NDPRF_ONLINK) ||
783 !(pr->ndpr_stateflags & NDPRF_IFSCOPE) ||
784 pr->ndpr_plen != pr_len ||
785 !in6_are_prefix_equal(p1: &pr->ndpr_prefix.sin6_addr, ifscope1: pr->ndpr_prefix.sin6_scope_id,
786 p2: &pr_addr, ifscope2: pr_ifscope, len: pr_len)) {
787 NDPR_UNLOCK(pr);
788 continue;
789 }
790 NDPR_UNLOCK(pr);
791
792 down = nd6_ndprl_alloc(how: Z_WAITOK);
793 if (down == NULL) {
794 continue;
795 }
796
797 NDPR_ADDREF(pr);
798 down->ndprl_pr = pr;
799 NDPR_ADDREF(pr_up);
800 down->ndprl_up = pr_up;
801 SLIST_INSERT_HEAD(&down_head, down, ndprl_le);
802 }
803 lck_mtx_unlock(nd6_mutex);
804
805 /*
806 * Apply routing function on prefixes; callee will free resources.
807 */
808 nd6_prproxy_prelist_setroute(enable,
809 up_head: (struct nd6_prproxy_prelist_head *)&up_head,
810 down_head: (struct nd6_prproxy_prelist_head *)&down_head);
811
812done:
813 VERIFY(SLIST_EMPTY(&up_head));
814 VERIFY(SLIST_EMPTY(&down_head));
815}
816
817/*
818 * Given an interface address, determine whether or not the address
819 * is part of of a proxied prefix.
820 */
821boolean_t
822nd6_prproxy_ifaddr(struct in6_ifaddr *ia)
823{
824 struct nd_prefix *pr;
825 struct in6_addr addr;
826 u_int32_t pr_len;
827 uint32_t pr_scope_id;
828 boolean_t proxied = FALSE;
829
830 LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED);
831
832 IFA_LOCK(&ia->ia_ifa);
833 bcopy(src: &ia->ia_addr.sin6_addr, dst: &addr, n: sizeof(addr));
834 pr_len = ia->ia_plen;
835 pr_scope_id = IA6_SIN6_SCOPE(ia);
836 IFA_UNLOCK(&ia->ia_ifa);
837
838 lck_mtx_lock(nd6_mutex);
839 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
840 NDPR_LOCK(pr);
841 if ((pr->ndpr_stateflags & NDPRF_ONLINK) &&
842 (pr->ndpr_stateflags & NDPRF_PRPROXY) &&
843 in6_are_prefix_equal(p1: &pr->ndpr_prefix.sin6_addr, ifscope1: pr->ndpr_prefix.sin6_scope_id,
844 p2: &addr, ifscope2: pr_scope_id, len: pr_len)) {
845 NDPR_UNLOCK(pr);
846 proxied = TRUE;
847 break;
848 }
849 NDPR_UNLOCK(pr);
850 }
851 lck_mtx_unlock(nd6_mutex);
852
853 return proxied;
854}
855
856/*
857 * Perform automatic proxy function with NS output.
858 *
859 * If the target address matches a global prefix obtained from a router
860 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
861 * flag set, then we send solicitations for the target address to all other
862 * interfaces where a matching prefix is currently on-link, in addition to
863 * the original interface.
864 */
865void
866nd6_prproxy_ns_output(struct ifnet *ifp, struct ifnet *exclifp,
867 struct in6_addr *daddr, struct in6_addr *taddr, struct llinfo_nd6 *ln)
868{
869 SLIST_HEAD(, nd6_prproxy_prelist) ndprl_head;
870 struct nd6_prproxy_prelist *ndprl, *ndprl_tmp;
871 struct nd_prefix *pr, *fwd;
872 struct ifnet *fwd_ifp;
873 struct in6_addr pr_addr;
874 u_char pr_len;
875 uint32_t pr_scope_id;
876 uint32_t taddr_ifscope = ifp->if_index;
877
878 /*
879 * Ignore excluded interface if it's the same as the original;
880 * we always send a NS on the original interface down below.
881 */
882 if (exclifp != NULL && exclifp == ifp) {
883 exclifp = NULL;
884 }
885
886 if (exclifp == NULL) {
887 nd6log2(debug, "%s: sending NS who has %s on ALL\n",
888 if_name(ifp), ip6_sprintf(taddr));
889 } else {
890 nd6log2(debug, "%s: sending NS who has %s on ALL "
891 "(except %s)\n", if_name(ifp),
892 ip6_sprintf(taddr), if_name(exclifp));
893 }
894
895 SLIST_INIT(&ndprl_head);
896
897 lck_mtx_lock(nd6_mutex);
898
899 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
900 NDPR_LOCK(pr);
901 pr_scope_id = pr->ndpr_prefix.sin6_scope_id;
902
903 if (!(pr->ndpr_stateflags & NDPRF_ONLINK) ||
904 !(pr->ndpr_stateflags & NDPRF_PRPROXY) ||
905 !in6_are_masked_addr_scope_equal(&pr->ndpr_prefix.sin6_addr, pr_scope_id,
906 taddr, taddr_ifscope, &pr->ndpr_mask)) {
907 NDPR_UNLOCK(pr);
908 continue;
909 }
910
911 VERIFY(!(pr->ndpr_stateflags & NDPRF_IFSCOPE));
912 bcopy(src: &pr->ndpr_prefix.sin6_addr, dst: &pr_addr, n: sizeof(pr_addr));
913 pr_len = pr->ndpr_plen;
914 NDPR_UNLOCK(pr);
915
916 for (fwd = nd_prefix.lh_first; fwd; fwd = fwd->ndpr_next) {
917 NDPR_LOCK(fwd);
918 if (!(fwd->ndpr_stateflags & NDPRF_ONLINK) ||
919 fwd->ndpr_ifp == ifp || fwd->ndpr_ifp == exclifp ||
920 fwd->ndpr_plen != pr_len ||
921 !in6_are_prefix_equal(p1: &fwd->ndpr_prefix.sin6_addr, ifscope1: fwd->ndpr_prefix.sin6_scope_id,
922 p2: &pr_addr, ifscope2: pr_scope_id, len: pr_len)) {
923 NDPR_UNLOCK(fwd);
924 continue;
925 }
926
927 fwd_ifp = fwd->ndpr_ifp;
928 NDPR_UNLOCK(fwd);
929
930 ndprl = nd6_ndprl_alloc(how: Z_WAITOK);
931 if (ndprl == NULL) {
932 continue;
933 }
934
935 NDPR_ADDREF(fwd);
936 ndprl->ndprl_pr = fwd;
937 ndprl->ndprl_fwd_ifp = fwd_ifp;
938
939 SLIST_INSERT_HEAD(&ndprl_head, ndprl, ndprl_le);
940 }
941 break;
942 }
943
944 lck_mtx_unlock(nd6_mutex);
945
946 SLIST_FOREACH_SAFE(ndprl, &ndprl_head, ndprl_le, ndprl_tmp) {
947 SLIST_REMOVE(&ndprl_head, ndprl, nd6_prproxy_prelist, ndprl_le);
948
949 pr = ndprl->ndprl_pr;
950 fwd_ifp = ndprl->ndprl_fwd_ifp;
951
952 if ((fwd_ifp->if_eflags & IFEF_IPV6_ND6ALT) != 0) {
953 NDPR_REMREF(pr);
954 nd6_ndprl_free(ndprl);
955 continue;
956 }
957
958 NDPR_LOCK(pr);
959 if (pr->ndpr_stateflags & NDPRF_ONLINK) {
960 NDPR_UNLOCK(pr);
961 nd6log2(debug,
962 "%s: Sending cloned NS who has %s, originally "
963 "on %s\n", if_name(fwd_ifp),
964 ip6_sprintf(taddr), if_name(ifp));
965
966 nd6_ns_output(fwd_ifp, daddr, taddr, NULL, NULL);
967 } else {
968 NDPR_UNLOCK(pr);
969 }
970 NDPR_REMREF(pr);
971
972 nd6_ndprl_free(ndprl);
973 }
974 VERIFY(SLIST_EMPTY(&ndprl_head));
975
976 nd6_ns_output(ifp, daddr, taddr, ln, NULL);
977}
978
979/*
980 * Perform automatic proxy function with NS input.
981 *
982 * If the target address matches a global prefix obtained from a router
983 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES
984 * flag set, then we send solicitations for the target address to all other
985 * interfaces where a matching prefix is currently on-link.
986 */
987void
988nd6_prproxy_ns_input(struct ifnet *ifp, struct in6_addr *saddr,
989 char *lladdr, int lladdrlen, struct in6_addr *daddr,
990 struct in6_addr *taddr, uint8_t *nonce)
991{
992 SLIST_HEAD(, nd6_prproxy_prelist) ndprl_head;
993 struct nd6_prproxy_prelist *ndprl, *ndprl_tmp;
994 struct nd_prefix *pr, *fwd;
995 struct ifnet *fwd_ifp;
996 struct in6_addr pr_addr;
997 u_char pr_len;
998 boolean_t solrec = FALSE;
999 uint32_t pr_scope_id;
1000 uint32_t taddr_ifscope = ifp->if_index;
1001
1002 SLIST_INIT(&ndprl_head);
1003
1004 lck_mtx_lock(nd6_mutex);
1005
1006 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
1007 NDPR_LOCK(pr);
1008 pr_scope_id = pr->ndpr_prefix.sin6_scope_id;
1009
1010 if (!(pr->ndpr_stateflags & NDPRF_ONLINK) ||
1011 !(pr->ndpr_stateflags & NDPRF_PRPROXY) ||
1012 !in6_are_masked_addr_scope_equal(&pr->ndpr_prefix.sin6_addr, pr_scope_id,
1013 taddr, taddr_ifscope, &pr->ndpr_mask)) {
1014 NDPR_UNLOCK(pr);
1015 continue;
1016 }
1017
1018 VERIFY(!(pr->ndpr_stateflags & NDPRF_IFSCOPE));
1019 bcopy(src: &pr->ndpr_prefix.sin6_addr, dst: &pr_addr, n: sizeof(pr_addr));
1020 pr_len = pr->ndpr_plen;
1021
1022 /*
1023 * If this is a NS for NUD/AR, record it so that we know
1024 * how to forward the NA reply later on (if/when it arrives.)
1025 * Give up if we fail to save the NS info.
1026 */
1027 if ((solrec = !IN6_IS_ADDR_UNSPECIFIED(saddr)) &&
1028 !nd6_solsrc_enq(pr, ifp, saddr, taddr)) {
1029 NDPR_UNLOCK(pr);
1030 solrec = FALSE;
1031 break; /* bail out */
1032 } else {
1033 NDPR_UNLOCK(pr);
1034 }
1035
1036 for (fwd = nd_prefix.lh_first; fwd; fwd = fwd->ndpr_next) {
1037 NDPR_LOCK(fwd);
1038 if (!(fwd->ndpr_stateflags & NDPRF_ONLINK) ||
1039 fwd->ndpr_ifp == ifp ||
1040 fwd->ndpr_plen != pr_len ||
1041 !in6_are_prefix_equal(p1: &fwd->ndpr_prefix.sin6_addr, ifscope1: fwd->ndpr_prefix.sin6_scope_id,
1042 p2: &pr_addr, ifscope2: pr_scope_id, len: pr_len)) {
1043 NDPR_UNLOCK(fwd);
1044 continue;
1045 }
1046
1047 fwd_ifp = fwd->ndpr_ifp;
1048 NDPR_UNLOCK(fwd);
1049
1050 ndprl = nd6_ndprl_alloc(how: Z_WAITOK);
1051 if (ndprl == NULL) {
1052 continue;
1053 }
1054
1055 NDPR_ADDREF(fwd);
1056 ndprl->ndprl_pr = fwd;
1057 ndprl->ndprl_fwd_ifp = fwd_ifp;
1058 ndprl->ndprl_sol = solrec;
1059
1060 SLIST_INSERT_HEAD(&ndprl_head, ndprl, ndprl_le);
1061 }
1062 break;
1063 }
1064
1065 lck_mtx_unlock(nd6_mutex);
1066
1067 /*
1068 * If this is a recorded solicitation (NS for NUD/AR), create
1069 * or update the neighbor cache entry for the soliciting node.
1070 * Later on, when the NA reply arrives, we will need this cache
1071 * entry in order to send the NA back to the original solicitor.
1072 * Without a neighbor cache entry, we'd end up with an endless
1073 * cycle of NS ping-pong between the us (the proxy) and the node
1074 * which is soliciting for the address.
1075 */
1076 if (solrec) {
1077 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr));
1078 nd6_cache_lladdr(ifp, saddr, lladdr, lladdrlen,
1079 ND_NEIGHBOR_SOLICIT, 0, NULL);
1080 }
1081
1082 SLIST_FOREACH_SAFE(ndprl, &ndprl_head, ndprl_le, ndprl_tmp) {
1083 SLIST_REMOVE(&ndprl_head, ndprl, nd6_prproxy_prelist, ndprl_le);
1084
1085 pr = ndprl->ndprl_pr;
1086 fwd_ifp = ndprl->ndprl_fwd_ifp;
1087
1088 if ((fwd_ifp->if_eflags & IFEF_IPV6_ND6ALT) != 0) {
1089 NDPR_REMREF(pr);
1090 nd6_ndprl_free(ndprl);
1091 continue;
1092 }
1093
1094 NDPR_LOCK(pr);
1095 if (pr->ndpr_stateflags & NDPRF_ONLINK) {
1096 NDPR_UNLOCK(pr);
1097 nd6log2(debug,
1098 "%s: Forwarding NS (%s) from %s to %s who "
1099 "has %s, originally on %s\n", if_name(fwd_ifp),
1100 ndprl->ndprl_sol ? "NUD/AR" :
1101 "DAD", ip6_sprintf(saddr), ip6_sprintf(daddr),
1102 ip6_sprintf(taddr), if_name(ifp));
1103
1104 nd6_ns_output(fwd_ifp, ndprl->ndprl_sol ? taddr : NULL,
1105 taddr, NULL, nonce);
1106 } else {
1107 NDPR_UNLOCK(pr);
1108 }
1109 NDPR_REMREF(pr);
1110
1111 nd6_ndprl_free(ndprl);
1112 }
1113 VERIFY(SLIST_EMPTY(&ndprl_head));
1114}
1115
1116/*
1117 * Perform automatic proxy function with NA input.
1118 *
1119 * If the target address matches a global prefix obtained from a router
1120 * advertisement received on an interface with the ND6_IFF_PROXY_PREFIXES flag
1121 * set, then we send neighbor advertisements for the target address on all
1122 * other interfaces where a matching prefix is currently on link.
1123 */
1124void
1125nd6_prproxy_na_input(struct ifnet *ifp, struct in6_addr *saddr,
1126 struct in6_addr *daddr0, struct in6_addr *taddr, int flags)
1127{
1128 SLIST_HEAD(, nd6_prproxy_prelist) ndprl_head;
1129 struct nd6_prproxy_prelist *ndprl, *ndprl_tmp;
1130 struct nd_prefix *pr;
1131 struct ifnet *fwd_ifp;
1132 struct in6_addr daddr;
1133 uint32_t pr_scope_id;
1134 uint32_t taddr_ifscope = ifp->if_index;
1135
1136 SLIST_INIT(&ndprl_head);
1137
1138 lck_mtx_lock(nd6_mutex);
1139
1140 for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
1141 NDPR_LOCK(pr);
1142
1143 pr_scope_id = pr->ndpr_prefix.sin6_scope_id;
1144 if (!(pr->ndpr_stateflags & NDPRF_ONLINK) ||
1145 !(pr->ndpr_stateflags & NDPRF_PRPROXY) ||
1146 !in6_are_masked_addr_scope_equal(&pr->ndpr_prefix.sin6_addr, pr_scope_id,
1147 taddr, taddr_ifscope, &pr->ndpr_mask)) {
1148 NDPR_UNLOCK(pr);
1149 continue;
1150 }
1151
1152 VERIFY(!(pr->ndpr_stateflags & NDPRF_IFSCOPE));
1153 /*
1154 * If this is a NA for NUD, see if there is a record created
1155 * for the corresponding NS; upon success, we get back the
1156 * interface where the NS originally arrived on, as well as
1157 * the soliciting node's address. Give up if we can't find it.
1158 */
1159 if (!IN6_IS_ADDR_MULTICAST(daddr0)) {
1160 fwd_ifp = NULL;
1161 bzero(s: &daddr, n: sizeof(daddr));
1162 if (!nd6_solsrc_deq(pr, taddr, &daddr, &fwd_ifp)) {
1163 NDPR_UNLOCK(pr);
1164 break; /* bail out */
1165 }
1166 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr) && fwd_ifp);
1167 NDPR_UNLOCK(pr);
1168
1169 ndprl = nd6_ndprl_alloc(how: Z_WAITOK);
1170 if (ndprl == NULL) {
1171 break; /* bail out */
1172 }
1173 ndprl->ndprl_fwd_ifp = fwd_ifp;
1174 ndprl->ndprl_sol = TRUE;
1175 ndprl->ndprl_sol_saddr = *(&daddr);
1176
1177 SLIST_INSERT_HEAD(&ndprl_head, ndprl, ndprl_le);
1178 } else {
1179 struct nd_prefix *fwd;
1180 struct in6_addr pr_addr;
1181 u_char pr_len;
1182
1183 bcopy(src: &pr->ndpr_prefix.sin6_addr, dst: &pr_addr,
1184 n: sizeof(pr_addr));
1185 pr_len = pr->ndpr_plen;
1186 NDPR_UNLOCK(pr);
1187
1188 for (fwd = nd_prefix.lh_first; fwd;
1189 fwd = fwd->ndpr_next) {
1190 NDPR_LOCK(fwd);
1191 if (!(fwd->ndpr_stateflags & NDPRF_ONLINK) ||
1192 fwd->ndpr_ifp == ifp ||
1193 fwd->ndpr_plen != pr_len ||
1194 !in6_are_prefix_equal(
1195 p1: &fwd->ndpr_prefix.sin6_addr, ifscope1: fwd->ndpr_prefix.sin6_scope_id,
1196 p2: &pr_addr, ifscope2: pr_scope_id, len: pr_len)) {
1197 NDPR_UNLOCK(fwd);
1198 continue;
1199 }
1200
1201 fwd_ifp = fwd->ndpr_ifp;
1202 NDPR_UNLOCK(fwd);
1203
1204 ndprl = nd6_ndprl_alloc(how: Z_WAITOK);
1205 if (ndprl == NULL) {
1206 continue;
1207 }
1208
1209 NDPR_ADDREF(fwd);
1210 ndprl->ndprl_pr = fwd;
1211 ndprl->ndprl_fwd_ifp = fwd_ifp;
1212
1213 SLIST_INSERT_HEAD(&ndprl_head, ndprl, ndprl_le);
1214 }
1215 }
1216 break;
1217 }
1218
1219 lck_mtx_unlock(nd6_mutex);
1220
1221 SLIST_FOREACH_SAFE(ndprl, &ndprl_head, ndprl_le, ndprl_tmp) {
1222 boolean_t send_na;
1223
1224 SLIST_REMOVE(&ndprl_head, ndprl, nd6_prproxy_prelist, ndprl_le);
1225
1226 pr = ndprl->ndprl_pr;
1227 fwd_ifp = ndprl->ndprl_fwd_ifp;
1228
1229 if (ndprl->ndprl_sol) {
1230 VERIFY(pr == NULL);
1231 daddr = *(&ndprl->ndprl_sol_saddr);
1232 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(&daddr));
1233 send_na = (in6_setscope(&daddr, fwd_ifp, NULL) == 0);
1234 } else {
1235 VERIFY(pr != NULL);
1236 daddr = *daddr0;
1237 NDPR_LOCK(pr);
1238 send_na = ((pr->ndpr_stateflags & NDPRF_ONLINK) &&
1239 in6_setscope(&daddr, fwd_ifp, NULL) == 0);
1240 NDPR_UNLOCK(pr);
1241 }
1242
1243 if (send_na) {
1244 if (!ndprl->ndprl_sol) {
1245 nd6log2(debug,
1246 "%s: Forwarding NA (DAD) from %s to %s "
1247 "tgt is %s, originally on %s\n",
1248 if_name(fwd_ifp),
1249 ip6_sprintf(saddr), ip6_sprintf(&daddr),
1250 ip6_sprintf(taddr), if_name(ifp));
1251 } else {
1252 nd6log2(debug,
1253 "%s: Forwarding NA (NUD/AR) from %s to "
1254 "%s (was %s) tgt is %s, originally on "
1255 "%s\n", if_name(fwd_ifp),
1256 ip6_sprintf(saddr),
1257 ip6_sprintf(&daddr), ip6_sprintf(daddr0),
1258 ip6_sprintf(taddr), if_name(ifp));
1259 }
1260
1261 nd6_na_output(fwd_ifp, &daddr, taddr, flags, 1, NULL);
1262 }
1263
1264 if (pr != NULL) {
1265 NDPR_REMREF(pr);
1266 }
1267
1268 nd6_ndprl_free(ndprl);
1269 }
1270 VERIFY(SLIST_EMPTY(&ndprl_head));
1271}
1272
1273static struct nd6_prproxy_solsrc *
1274nd6_solsrc_alloc(int how)
1275{
1276 return zalloc_flags(solsrc_zone, how | Z_ZERO);
1277}
1278
1279static void
1280nd6_solsrc_free(struct nd6_prproxy_solsrc *ssrc)
1281{
1282 zfree(solsrc_zone, ssrc);
1283}
1284
1285static void
1286nd6_prproxy_sols_purge(struct nd_prefix *pr, u_int64_t max_stgt)
1287{
1288 struct nd6_prproxy_soltgt *soltgt, *tmp;
1289 u_int64_t expire = (max_stgt > 0) ? net_uptime() : 0;
1290
1291 NDPR_LOCK_ASSERT_HELD(pr);
1292
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);
1303 }
1304 }
1305
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)));
1309 return;
1310 }
1311
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) {
1320 break;
1321 }
1322 }
1323}
1324
1325/*
1326 * Purges all solicitation records on a given prefix.
1327 * Caller is responsible for holding prefix lock.
1328 */
1329void
1330nd6_prproxy_sols_reap(struct nd_prefix *pr)
1331{
1332 nd6_prproxy_sols_purge(pr, max_stgt: 0);
1333}
1334
1335/*
1336 * Purges expired or idle solicitation records on a given prefix.
1337 * Caller is responsible for holding prefix lock.
1338 */
1339void
1340nd6_prproxy_sols_prune(struct nd_prefix *pr, u_int32_t max_stgt)
1341{
1342 nd6_prproxy_sols_purge(pr, max_stgt);
1343}
1344
1345/*
1346 * Enqueue a soliciation record in the target record of a prefix.
1347 */
1348static boolean_t
1349nd6_solsrc_enq(struct nd_prefix *pr, struct ifnet *ifp,
1350 struct in6_addr *saddr, struct in6_addr *taddr)
1351{
1352 struct nd6_prproxy_soltgt find, *soltgt;
1353 struct nd6_prproxy_solsrc *ssrc;
1354 u_int32_t max_stgt = nd6_max_tgt_sols;
1355 u_int32_t max_ssrc = nd6_max_src_sols;
1356
1357 NDPR_LOCK_ASSERT_HELD(pr);
1358 VERIFY(!(pr->ndpr_stateflags & NDPRF_IFSCOPE));
1359 VERIFY((pr->ndpr_stateflags & (NDPRF_ONLINK | NDPRF_PRPROXY)) ==
1360 (NDPRF_ONLINK | NDPRF_PRPROXY));
1361 VERIFY(!IN6_IS_ADDR_UNSPECIFIED(saddr));
1362
1363 ssrc = nd6_solsrc_alloc(M_WAITOK);
1364 if (ssrc == NULL) {
1365 return FALSE;
1366 }
1367
1368 ssrc->solsrc_saddr = *saddr;
1369 ssrc->solsrc_ifp = ifp;
1370
1371 find.soltgt_key.taddr = *taddr; /* search key */
1372
1373 soltgt = RB_FIND(prproxy_sols_tree, &pr->ndpr_prproxy_sols, &find);
1374 if (soltgt == NULL) {
1375 if (max_stgt != 0 && pr->ndpr_prproxy_sols_cnt >= max_stgt) {
1376 VERIFY(!RB_EMPTY(&pr->ndpr_prproxy_sols));
1377 nd6_prproxy_sols_prune(pr, max_stgt);
1378 VERIFY(pr->ndpr_prproxy_sols_cnt < max_stgt);
1379 }
1380
1381 soltgt = nd6_soltgt_alloc(M_WAITOK);
1382 if (soltgt == NULL) {
1383 nd6_solsrc_free(ssrc);
1384 return FALSE;
1385 }
1386
1387 soltgt->soltgt_key.taddr = *taddr;
1388 VERIFY(soltgt->soltgt_cnt == 0);
1389 VERIFY(TAILQ_EMPTY(&soltgt->soltgt_q));
1390
1391 pr->ndpr_prproxy_sols_cnt++;
1392 VERIFY(pr->ndpr_prproxy_sols_cnt != 0);
1393 RB_INSERT(prproxy_sols_tree, &pr->ndpr_prproxy_sols, soltgt);
1394 }
1395
1396 if (max_ssrc != 0 && soltgt->soltgt_cnt >= max_ssrc) {
1397 VERIFY(!TAILQ_EMPTY(&soltgt->soltgt_q));
1398 nd6_soltgt_prune(soltgt, max_ssrc);
1399 VERIFY(soltgt->soltgt_cnt < max_ssrc);
1400 }
1401
1402 soltgt->soltgt_cnt++;
1403 VERIFY(soltgt->soltgt_cnt != 0);
1404 TAILQ_INSERT_TAIL(&soltgt->soltgt_q, ssrc, solsrc_tqe);
1405 if (soltgt->soltgt_cnt == 1) {
1406 soltgt->soltgt_expire = net_uptime() + ND6_TGT_SOLS_EXPIRE;
1407 }
1408
1409 return TRUE;
1410}
1411
1412/*
1413 * Dequeue a solicitation record from a target record of a prefix.
1414 */
1415static boolean_t
1416nd6_solsrc_deq(struct nd_prefix *pr, struct in6_addr *taddr,
1417 struct in6_addr *daddr, struct ifnet **ifp)
1418{
1419 struct nd6_prproxy_soltgt find, *soltgt;
1420 struct nd6_prproxy_solsrc *ssrc;
1421
1422 NDPR_LOCK_ASSERT_HELD(pr);
1423 VERIFY(!(pr->ndpr_stateflags & NDPRF_IFSCOPE));
1424 VERIFY((pr->ndpr_stateflags & (NDPRF_ONLINK | NDPRF_PRPROXY)) ==
1425 (NDPRF_ONLINK | NDPRF_PRPROXY));
1426
1427 bzero(s: daddr, n: sizeof(*daddr));
1428 *ifp = NULL;
1429
1430 find.soltgt_key.taddr = *taddr; /* search key */
1431
1432 soltgt = RB_FIND(prproxy_sols_tree, &pr->ndpr_prproxy_sols, &find);
1433 if (soltgt == NULL || soltgt->soltgt_cnt == 0) {
1434 VERIFY(soltgt == NULL || TAILQ_EMPTY(&soltgt->soltgt_q));
1435 return FALSE;
1436 }
1437
1438 VERIFY(soltgt->soltgt_cnt != 0);
1439 --soltgt->soltgt_cnt;
1440 ssrc = TAILQ_FIRST(&soltgt->soltgt_q);
1441 VERIFY(ssrc != NULL);
1442 TAILQ_REMOVE(&soltgt->soltgt_q, ssrc, solsrc_tqe);
1443 *daddr = *(&ssrc->solsrc_saddr);
1444 *ifp = ssrc->solsrc_ifp;
1445 nd6_solsrc_free(ssrc);
1446
1447 return TRUE;
1448}
1449
1450static struct nd6_prproxy_soltgt *
1451nd6_soltgt_alloc(int how)
1452{
1453 struct nd6_prproxy_soltgt *soltgt;
1454
1455 soltgt = zalloc_flags(soltgt_zone, how | Z_ZERO);
1456 if (soltgt != NULL) {
1457 TAILQ_INIT(&soltgt->soltgt_q);
1458 }
1459 return soltgt;
1460}
1461
1462static void
1463nd6_soltgt_free(struct nd6_prproxy_soltgt *soltgt)
1464{
1465 struct nd6_prproxy_solsrc *ssrc, *tssrc;
1466
1467 TAILQ_FOREACH_SAFE(ssrc, &soltgt->soltgt_q, solsrc_tqe, tssrc) {
1468 VERIFY(soltgt->soltgt_cnt > 0);
1469 soltgt->soltgt_cnt--;
1470 TAILQ_REMOVE(&soltgt->soltgt_q, ssrc, solsrc_tqe);
1471 nd6_solsrc_free(ssrc);
1472 }
1473
1474 VERIFY(soltgt->soltgt_cnt == 0);
1475 VERIFY(TAILQ_EMPTY(&soltgt->soltgt_q));
1476
1477 zfree(soltgt_zone, soltgt);
1478}
1479
1480static void
1481nd6_soltgt_prune(struct nd6_prproxy_soltgt *soltgt, u_int32_t max_ssrc)
1482{
1483 while (soltgt->soltgt_cnt >= max_ssrc) {
1484 struct nd6_prproxy_solsrc *ssrc;
1485
1486 VERIFY(soltgt->soltgt_cnt != 0);
1487 --soltgt->soltgt_cnt;
1488 ssrc = TAILQ_FIRST(&soltgt->soltgt_q);
1489 VERIFY(ssrc != NULL);
1490 TAILQ_REMOVE(&soltgt->soltgt_q, ssrc, solsrc_tqe);
1491 nd6_solsrc_free(ssrc);
1492 }
1493}
1494
1495/*
1496 * Solicited target tree comparison function.
1497 *
1498 * An ordered predicate is necessary; bcmp() is not documented to return
1499 * an indication of order, memcmp() is, and is an ISO C99 requirement.
1500 */
1501static __inline int
1502soltgt_cmp(const struct nd6_prproxy_soltgt *a,
1503 const struct nd6_prproxy_soltgt *b)
1504{
1505 return memcmp(s1: &a->soltgt_key, s2: &b->soltgt_key, n: sizeof(a->soltgt_key));
1506}
1507