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