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