xref: /xnu-10002.61.3/bsd/netinet6/nd6_prproxy.c (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
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 
109 struct nd6_prproxy_prelist {
110 	SLIST_ENTRY(nd6_prproxy_prelist) ndprl_le;
111 	struct nd_prefix *ndprl_pr;             /* prefix */
112 	struct nd_prefix *ndprl_up;             /* non-NULL for upstream */
113 	struct ifnet    *ndprl_fwd_ifp;         /* outgoing interface */
114 	boolean_t       ndprl_sol;              /* unicast solicitor? */
115 	struct in6_addr ndprl_sol_saddr;        /* solicitor's address */
116 };
117 
118 /*
119  * Soliciting node (source) record.
120  */
121 struct nd6_prproxy_solsrc {
122 	TAILQ_ENTRY(nd6_prproxy_solsrc) solsrc_tqe;
123 	struct in6_addr solsrc_saddr;           /* soliciting (src) address */
124 	struct ifnet    *solsrc_ifp;            /* iface where NS arrived on */
125 };
126 
127 /*
128  * Solicited node (target) record.
129  */
130 struct nd6_prproxy_soltgt {
131 	RB_ENTRY(nd6_prproxy_soltgt) soltgt_link; /* RB tree links */
132 	struct soltgt_key_s {
133 		struct in6_addr taddr;          /* solicited (tgt) address */
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 
140 SLIST_HEAD(nd6_prproxy_prelist_head, nd6_prproxy_prelist);
141 
142 static void nd6_prproxy_prelist_setroute(boolean_t enable,
143     struct nd6_prproxy_prelist_head *, struct nd6_prproxy_prelist_head *);
144 static struct nd6_prproxy_prelist *nd6_ndprl_alloc(zalloc_flags_t);
145 static void nd6_ndprl_free(struct nd6_prproxy_prelist *);
146 static struct nd6_prproxy_solsrc *nd6_solsrc_alloc(int);
147 static void nd6_solsrc_free(struct nd6_prproxy_solsrc *);
148 static boolean_t nd6_solsrc_enq(struct nd_prefix *, struct ifnet *,
149     struct in6_addr *, struct in6_addr *);
150 static boolean_t nd6_solsrc_deq(struct nd_prefix *, struct in6_addr *,
151     struct in6_addr *, struct ifnet **);
152 static struct nd6_prproxy_soltgt *nd6_soltgt_alloc(int);
153 static void nd6_soltgt_free(struct nd6_prproxy_soltgt *);
154 static void nd6_soltgt_prune(struct nd6_prproxy_soltgt *, u_int32_t);
155 static __inline int soltgt_cmp(const struct nd6_prproxy_soltgt *,
156     const struct nd6_prproxy_soltgt *);
157 static void nd6_prproxy_sols_purge(struct nd_prefix *, u_int64_t);
158 
159 RB_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 
177 static u_int32_t nd6_max_tgt_sols = ND6_MAX_TGT_SOLS_DEFAULT;
178 static u_int32_t nd6_max_src_sols = ND6_MAX_SRC_SOLS_DEFAULT;
179 
180 static KALLOC_TYPE_DEFINE(ndprl_zone,
181     struct nd6_prproxy_prelist, NET_KT_DEFAULT);    /* nd6_prproxy_prelist zone */
182 
183 static KALLOC_TYPE_DEFINE(solsrc_zone,
184     struct nd6_prproxy_solsrc, NET_KT_DEFAULT);     /* nd6_prproxy_solsrc zone */
185 
186 static 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 */
190 RB_GENERATE_PREV(prproxy_sols_tree, nd6_prproxy_soltgt,
191     soltgt_link, soltgt_cmp);
192 
193 /* The following is protected by proxy6_lock (for updates) */
194 u_int32_t nd6_prproxy;
195 
196 SYSCTL_DECL(_net_inet6_icmp6);
197 
198 SYSCTL_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 
202 SYSCTL_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 
206 SYSCTL_UINT(_net_inet6_icmp6, OID_AUTO, prproxy_cnt,
207     CTLFLAG_RD | CTLFLAG_LOCKED, &nd6_prproxy, 0,
208     "total number of proxied prefixes");
209 
210 static struct nd6_prproxy_prelist *
nd6_ndprl_alloc(zalloc_flags_t how)211 nd6_ndprl_alloc(zalloc_flags_t how)
212 {
213 	return zalloc_flags(ndprl_zone, how | Z_ZERO);
214 }
215 
216 static void
nd6_ndprl_free(struct nd6_prproxy_prelist * ndprl)217 nd6_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  */
228 static void
nd6_prproxy_prelist_setroute(boolean_t enable,struct nd6_prproxy_prelist_head * up_head,struct nd6_prproxy_prelist_head * down_head)229 nd6_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(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(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  */
354 int
nd6_if_prproxy(struct ifnet * ifp,boolean_t enable)355 nd6_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(&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(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(&pr->ndpr_prefix.sin6_addr, &pr_addr, 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(&fwd->ndpr_prefix.sin6_addr, fwd->ndpr_prefix.sin6_scope_id,
454 			    &pr_addr, pr_ifscope, pr_len)) {
455 				NDPR_UNLOCK(fwd);
456 				continue;
457 			}
458 			NDPR_UNLOCK(fwd);
459 
460 			down = nd6_ndprl_alloc(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 	    (struct nd6_prproxy_prelist_head *)&up_head,
479 	    (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(&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  */
494 boolean_t
nd6_prproxy_isours(struct mbuf * m,struct ip6_hdr * ip6,struct route_in6 * ro6,unsigned int ifscope)495 nd6_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 
544 done:
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  */
557 void
nd6_proxy_find_fwdroute(struct ifnet * ifp,struct route_in6 * ro6)558 nd6_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(&pr->ndpr_prefix.sin6_addr, &pr_addr, 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(&fwd->ndpr_prefix.sin6_addr, fwd->ndpr_prefix.sin6_scope_id,
621 			    &pr_addr, pr_ifscope, 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  */
697 void
nd6_prproxy_prelist_update(struct nd_prefix * pr_cur,struct nd_prefix * pr_up)698 nd6_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(&pr_cur->ndpr_prefix.sin6_addr, &pr_addr,
724 		    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(&pr->ndpr_prefix.sin6_addr, pr->ndpr_prefix.sin6_scope_id,
735 			    &pr_addr, pr_ifscope, 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(&pr_up->ndpr_prefix.sin6_addr, &pr_addr,
751 		    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(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(&pr->ndpr_prefix.sin6_addr, pr->ndpr_prefix.sin6_scope_id,
786 		    &pr_addr, pr_ifscope, pr_len)) {
787 			NDPR_UNLOCK(pr);
788 			continue;
789 		}
790 		NDPR_UNLOCK(pr);
791 
792 		down = nd6_ndprl_alloc(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 	    (struct nd6_prproxy_prelist_head *)&up_head,
810 	    (struct nd6_prproxy_prelist_head *)&down_head);
811 
812 done:
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  */
821 boolean_t
nd6_prproxy_ifaddr(struct in6_ifaddr * ia)822 nd6_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(&ia->ia_addr.sin6_addr, &addr, 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(&pr->ndpr_prefix.sin6_addr, pr->ndpr_prefix.sin6_scope_id,
844 		    &addr, pr_scope_id, 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  */
865 void
nd6_prproxy_ns_output(struct ifnet * ifp,struct ifnet * exclifp,struct in6_addr * daddr,struct in6_addr * taddr,struct llinfo_nd6 * ln)866 nd6_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(&pr->ndpr_prefix.sin6_addr, &pr_addr, 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(&fwd->ndpr_prefix.sin6_addr, fwd->ndpr_prefix.sin6_scope_id,
922 			    &pr_addr, pr_scope_id, 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(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  */
987 void
nd6_prproxy_ns_input(struct ifnet * ifp,struct in6_addr * saddr,char * lladdr,int lladdrlen,struct in6_addr * daddr,struct in6_addr * taddr,uint8_t * nonce)988 nd6_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(&pr->ndpr_prefix.sin6_addr, &pr_addr, 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(&fwd->ndpr_prefix.sin6_addr, fwd->ndpr_prefix.sin6_scope_id,
1042 			    &pr_addr, pr_scope_id, 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(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  */
1124 void
nd6_prproxy_na_input(struct ifnet * ifp,struct in6_addr * saddr,struct in6_addr * daddr0,struct in6_addr * taddr,int flags)1125 nd6_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(&daddr, 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(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(&pr->ndpr_prefix.sin6_addr, &pr_addr,
1184 			    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 					    &fwd->ndpr_prefix.sin6_addr, fwd->ndpr_prefix.sin6_scope_id,
1196 					    &pr_addr, pr_scope_id, 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(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 
1273 static struct nd6_prproxy_solsrc *
nd6_solsrc_alloc(int how)1274 nd6_solsrc_alloc(int how)
1275 {
1276 	return zalloc_flags(solsrc_zone, how | Z_ZERO);
1277 }
1278 
1279 static void
nd6_solsrc_free(struct nd6_prproxy_solsrc * ssrc)1280 nd6_solsrc_free(struct nd6_prproxy_solsrc *ssrc)
1281 {
1282 	zfree(solsrc_zone, ssrc);
1283 }
1284 
1285 static void
nd6_prproxy_sols_purge(struct nd_prefix * pr,u_int64_t max_stgt)1286 nd6_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  */
1329 void
nd6_prproxy_sols_reap(struct nd_prefix * pr)1330 nd6_prproxy_sols_reap(struct nd_prefix *pr)
1331 {
1332 	nd6_prproxy_sols_purge(pr, 0);
1333 }
1334 
1335 /*
1336  * Purges expired or idle solicitation records on a given prefix.
1337  * Caller is responsible for holding prefix lock.
1338  */
1339 void
nd6_prproxy_sols_prune(struct nd_prefix * pr,u_int32_t max_stgt)1340 nd6_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  */
1348 static boolean_t
nd6_solsrc_enq(struct nd_prefix * pr,struct ifnet * ifp,struct in6_addr * saddr,struct in6_addr * taddr)1349 nd6_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  */
1415 static boolean_t
nd6_solsrc_deq(struct nd_prefix * pr,struct in6_addr * taddr,struct in6_addr * daddr,struct ifnet ** ifp)1416 nd6_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(daddr, 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 
1450 static struct nd6_prproxy_soltgt *
nd6_soltgt_alloc(int how)1451 nd6_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 
1462 static void
nd6_soltgt_free(struct nd6_prproxy_soltgt * soltgt)1463 nd6_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 
1480 static void
nd6_soltgt_prune(struct nd6_prproxy_soltgt * soltgt,u_int32_t max_ssrc)1481 nd6_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  */
1501 static __inline int
soltgt_cmp(const struct nd6_prproxy_soltgt * a,const struct nd6_prproxy_soltgt * b)1502 soltgt_cmp(const struct nd6_prproxy_soltgt *a,
1503     const struct nd6_prproxy_soltgt *b)
1504 {
1505 	return memcmp(&a->soltgt_key, &b->soltgt_key, sizeof(a->soltgt_key));
1506 }
1507