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