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