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