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