1 | /* |
2 | * Copyright (c) 2003-2018 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 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
31 | * All rights reserved. |
32 | * |
33 | * Redistribution and use in source and binary forms, with or without |
34 | * modification, are permitted provided that the following conditions |
35 | * are met: |
36 | * 1. Redistributions of source code must retain the above copyright |
37 | * notice, this list of conditions and the following disclaimer. |
38 | * 2. Redistributions in binary form must reproduce the above copyright |
39 | * notice, this list of conditions and the following disclaimer in the |
40 | * documentation and/or other materials provided with the distribution. |
41 | * 3. Neither the name of the project nor the names of its contributors |
42 | * may be used to endorse or promote products derived from this software |
43 | * without specific prior written permission. |
44 | * |
45 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
55 | * SUCH DAMAGE. |
56 | */ |
57 | |
58 | |
59 | #include <sys/param.h> |
60 | #include <sys/systm.h> |
61 | #include <sys/malloc.h> |
62 | #include <sys/mbuf.h> |
63 | #include <sys/socket.h> |
64 | #include <sys/sockio.h> |
65 | #include <sys/time.h> |
66 | #include <sys/kernel.h> |
67 | #include <sys/errno.h> |
68 | #include <sys/syslog.h> |
69 | #include <sys/queue.h> |
70 | #include <sys/mcache.h> |
71 | #include <sys/protosw.h> |
72 | |
73 | #include <dev/random/randomdev.h> |
74 | |
75 | #include <kern/locks.h> |
76 | #include <kern/zalloc.h> |
77 | #include <machine/machine_routines.h> |
78 | |
79 | #include <net/if.h> |
80 | #include <net/if_var.h> |
81 | #include <net/if_types.h> |
82 | #include <net/if_dl.h> |
83 | #include <net/route.h> |
84 | #include <net/radix.h> |
85 | |
86 | #include <netinet/in.h> |
87 | #include <netinet6/in6_var.h> |
88 | #include <netinet6/in6_ifattach.h> |
89 | #include <netinet/ip6.h> |
90 | #include <netinet6/ip6_var.h> |
91 | #include <netinet6/nd6.h> |
92 | #include <netinet/icmp6.h> |
93 | #include <netinet6/scope6_var.h> |
94 | |
95 | #include <net/net_osdep.h> |
96 | |
97 | static void defrouter_addreq(struct nd_defrouter *, boolean_t); |
98 | static void defrouter_delreq(struct nd_defrouter *); |
99 | static struct nd_defrouter *defrtrlist_update_common(struct nd_defrouter *, |
100 | boolean_t); |
101 | static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); |
102 | |
103 | static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *, |
104 | struct nd_defrouter *); |
105 | static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *); |
106 | static void pfxrtr_del(struct nd_pfxrouter *, struct nd_prefix *); |
107 | static struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *); |
108 | static void nd6_rtmsg(int, struct rtentry *); |
109 | |
110 | static int nd6_prefix_onlink_common(struct nd_prefix *, boolean_t, |
111 | unsigned int); |
112 | static struct nd_prefix *nd6_prefix_equal_lookup(struct nd_prefix *, boolean_t); |
113 | static void nd6_prefix_sync(struct ifnet *); |
114 | |
115 | static void in6_init_address_ltimes(struct nd_prefix *, |
116 | struct in6_addrlifetime *); |
117 | |
118 | static int rt6_deleteroute(struct radix_node *, void *); |
119 | |
120 | static struct nd_defrouter *nddr_alloc(int); |
121 | static void nddr_free(struct nd_defrouter *); |
122 | static void nddr_trace(struct nd_defrouter *, int); |
123 | |
124 | static struct nd_prefix *ndpr_alloc(int); |
125 | static void ndpr_free(struct nd_prefix *); |
126 | static void ndpr_trace(struct nd_prefix *, int); |
127 | |
128 | extern int nd6_recalc_reachtm_interval; |
129 | |
130 | static struct ifnet *nd6_defifp = NULL; |
131 | int nd6_defifindex = 0; |
132 | static unsigned int nd6_defrouter_genid; |
133 | |
134 | int ip6_use_tempaddr = 1; /* use temp addr by default for testing now */ |
135 | |
136 | int nd6_accept_6to4 = 1; |
137 | |
138 | int ip6_desync_factor; |
139 | u_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME; |
140 | u_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME; |
141 | /* |
142 | * shorter lifetimes for debugging purposes. |
143 | * u_int32_t ip6_temp_preferred_lifetime = 800; |
144 | * static u_int32_t ip6_temp_valid_lifetime = 1800; |
145 | */ |
146 | int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE; |
147 | |
148 | extern lck_mtx_t *nd6_mutex; |
149 | |
150 | /* Serialization variables for single thread access to nd_prefix */ |
151 | static boolean_t nd_prefix_busy; |
152 | static void *nd_prefix_waitchan = &nd_prefix_busy; |
153 | static int nd_prefix_waiters = 0; |
154 | |
155 | /* Serialization variables for single thread access to nd_defrouter */ |
156 | static boolean_t nd_defrouter_busy; |
157 | static void *nd_defrouter_waitchan = &nd_defrouter_busy; |
158 | static int nd_defrouter_waiters = 0; |
159 | |
160 | /* RTPREF_MEDIUM has to be 0! */ |
161 | #define RTPREF_HIGH 1 |
162 | #define RTPREF_MEDIUM 0 |
163 | #define RTPREF_LOW (-1) |
164 | #define RTPREF_RESERVED (-2) |
165 | #define RTPREF_INVALID (-3) /* internal */ |
166 | |
167 | #define NDPR_TRACE_HIST_SIZE 32 /* size of trace history */ |
168 | |
169 | /* For gdb */ |
170 | __private_extern__ unsigned int ndpr_trace_hist_size = NDPR_TRACE_HIST_SIZE; |
171 | |
172 | struct nd_prefix_dbg { |
173 | struct nd_prefix ndpr_pr; /* nd_prefix */ |
174 | u_int16_t ndpr_refhold_cnt; /* # of ref */ |
175 | u_int16_t ndpr_refrele_cnt; /* # of rele */ |
176 | /* |
177 | * Circular lists of ndpr_addref and ndpr_remref callers. |
178 | */ |
179 | ctrace_t ndpr_refhold[NDPR_TRACE_HIST_SIZE]; |
180 | ctrace_t ndpr_refrele[NDPR_TRACE_HIST_SIZE]; |
181 | }; |
182 | |
183 | static unsigned int ndpr_debug; /* debug flags */ |
184 | static unsigned int ndpr_size; /* size of zone element */ |
185 | static struct zone *ndpr_zone; /* zone for nd_prefix */ |
186 | |
187 | #define NDPR_ZONE_MAX 64 /* maximum elements in zone */ |
188 | #define NDPR_ZONE_NAME "nd6_prefix" /* zone name */ |
189 | |
190 | #define NDDR_TRACE_HIST_SIZE 32 /* size of trace history */ |
191 | |
192 | /* For gdb */ |
193 | __private_extern__ unsigned int nddr_trace_hist_size = NDDR_TRACE_HIST_SIZE; |
194 | |
195 | struct nd_defrouter_dbg { |
196 | struct nd_defrouter nddr_dr; /* nd_defrouter */ |
197 | uint16_t nddr_refhold_cnt; /* # of ref */ |
198 | uint16_t nddr_refrele_cnt; /* # of rele */ |
199 | /* |
200 | * Circular lists of ndpr_addref and ndpr_remref callers. |
201 | */ |
202 | ctrace_t nddr_refhold[NDDR_TRACE_HIST_SIZE]; |
203 | ctrace_t nddr_refrele[NDDR_TRACE_HIST_SIZE]; |
204 | }; |
205 | |
206 | static unsigned int nddr_debug; /* debug flags */ |
207 | static unsigned int nddr_size; /* size of zone element */ |
208 | static struct zone *nddr_zone; /* zone for nd_defrouter */ |
209 | |
210 | #define NDDR_ZONE_MAX 64 /* maximum elements in zone */ |
211 | #define NDDR_ZONE_NAME "nd6_defrouter" /* zone name */ |
212 | |
213 | static unsigned int ndprtr_size; /* size of zone element */ |
214 | static struct zone *ndprtr_zone; /* zone for nd_pfxrouter */ |
215 | |
216 | #define NDPRTR_ZONE_MAX 64 /* maximum elements in zone */ |
217 | #define NDPRTR_ZONE_NAME "nd6_pfxrouter" /* zone name */ |
218 | |
219 | void |
220 | nd6_rtr_init(void) |
221 | { |
222 | PE_parse_boot_argn("ifa_debug" , &ndpr_debug, sizeof (ndpr_debug)); |
223 | PE_parse_boot_argn("ifa_debug" , &nddr_debug, sizeof (nddr_debug)); |
224 | |
225 | ndpr_size = (ndpr_debug == 0) ? sizeof (struct nd_prefix) : |
226 | sizeof (struct nd_prefix_dbg); |
227 | ndpr_zone = zinit(ndpr_size, NDPR_ZONE_MAX * ndpr_size, 0, |
228 | NDPR_ZONE_NAME); |
229 | if (ndpr_zone == NULL) { |
230 | panic("%s: failed allocating %s" , __func__, NDPR_ZONE_NAME); |
231 | /* NOTREACHED */ |
232 | } |
233 | zone_change(ndpr_zone, Z_EXPAND, TRUE); |
234 | zone_change(ndpr_zone, Z_CALLERACCT, FALSE); |
235 | |
236 | nddr_size = (nddr_debug == 0) ? sizeof (struct nd_defrouter) : |
237 | sizeof (struct nd_defrouter_dbg); |
238 | nddr_zone = zinit(nddr_size, NDDR_ZONE_MAX * nddr_size, 0, |
239 | NDDR_ZONE_NAME); |
240 | if (nddr_zone == NULL) { |
241 | panic("%s: failed allocating %s" , __func__, NDDR_ZONE_NAME); |
242 | /* NOTREACHED */ |
243 | } |
244 | zone_change(nddr_zone, Z_EXPAND, TRUE); |
245 | zone_change(nddr_zone, Z_CALLERACCT, FALSE); |
246 | |
247 | ndprtr_size = sizeof (struct nd_pfxrouter); |
248 | ndprtr_zone = zinit(ndprtr_size, NDPRTR_ZONE_MAX * ndprtr_size, 0, |
249 | NDPRTR_ZONE_NAME); |
250 | if (ndprtr_zone == NULL) { |
251 | panic("%s: failed allocating %s" , __func__, NDPRTR_ZONE_NAME); |
252 | /* NOTREACHED */ |
253 | } |
254 | zone_change(ndprtr_zone, Z_EXPAND, TRUE); |
255 | zone_change(ndprtr_zone, Z_CALLERACCT, FALSE); |
256 | } |
257 | |
258 | /* |
259 | * Receive Router Solicitation Message - just for routers. |
260 | * Router solicitation/advertisement is mostly managed by userland program |
261 | * (rtadvd) so here we have no function like nd6_ra_output(). |
262 | * |
263 | * Based on RFC 2461 |
264 | */ |
265 | void |
266 | nd6_rs_input( |
267 | struct mbuf *m, |
268 | int off, |
269 | int icmp6len) |
270 | { |
271 | struct ifnet *ifp = m->m_pkthdr.rcvif; |
272 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
273 | struct nd_router_solicit *nd_rs; |
274 | struct in6_addr saddr6 = ip6->ip6_src; |
275 | char *lladdr = NULL; |
276 | int lladdrlen = 0; |
277 | union nd_opts ndopts; |
278 | |
279 | /* Expect 32-bit aligned data pointer on strict-align platforms */ |
280 | MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); |
281 | |
282 | /* If I'm not a router, ignore it. */ |
283 | if (!ip6_forwarding || !(ifp->if_eflags & IFEF_IPV6_ROUTER)) |
284 | goto freeit; |
285 | |
286 | /* Sanity checks */ |
287 | if (ip6->ip6_hlim != 255) { |
288 | nd6log((LOG_ERR, |
289 | "nd6_rs_input: invalid hlim (%d) from %s to %s on %s\n" , |
290 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), |
291 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); |
292 | goto bad; |
293 | } |
294 | |
295 | /* |
296 | * Don't update the neighbor cache, if src = :: or a non-neighbor. |
297 | * The former case indicates that the src has no IP address assigned |
298 | * yet. See nd6_ns_input() for the latter case. |
299 | */ |
300 | if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { |
301 | goto freeit; |
302 | } else { |
303 | struct sockaddr_in6 src_sa6; |
304 | |
305 | bzero(&src_sa6, sizeof (src_sa6)); |
306 | src_sa6.sin6_family = AF_INET6; |
307 | src_sa6.sin6_len = sizeof (src_sa6); |
308 | src_sa6.sin6_addr = ip6->ip6_src; |
309 | if (!nd6_is_addr_neighbor(&src_sa6, ifp, 0)) { |
310 | nd6log((LOG_INFO, "nd6_rs_input: " |
311 | "RS packet from non-neighbor\n" )); |
312 | goto freeit; |
313 | } |
314 | } |
315 | |
316 | IP6_EXTHDR_CHECK(m, off, icmp6len, return); |
317 | nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off); |
318 | icmp6len -= sizeof (*nd_rs); |
319 | nd6_option_init(nd_rs + 1, icmp6len, &ndopts); |
320 | if (nd6_options(&ndopts) < 0) { |
321 | nd6log((LOG_INFO, |
322 | "nd6_rs_input: invalid ND option, ignored\n" )); |
323 | /* nd6_options have incremented stats */ |
324 | goto freeit; |
325 | } |
326 | |
327 | if (ndopts.nd_opts_src_lladdr) { |
328 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); |
329 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; |
330 | } |
331 | |
332 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
333 | nd6log((LOG_INFO, |
334 | "nd6_rs_input: lladdrlen mismatch for %s " |
335 | "(if %d, RS packet %d)\n" , |
336 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); |
337 | goto bad; |
338 | } |
339 | |
340 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); |
341 | |
342 | freeit: |
343 | m_freem(m); |
344 | return; |
345 | |
346 | bad: |
347 | icmp6stat.icp6s_badrs++; |
348 | m_freem(m); |
349 | } |
350 | |
351 | /* |
352 | * Receive Router Advertisement Message. |
353 | * |
354 | * Based on RFC 2461 |
355 | * TODO: on-link bit on prefix information |
356 | * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing |
357 | */ |
358 | void |
359 | nd6_ra_input( |
360 | struct mbuf *m, |
361 | int off, |
362 | int icmp6len) |
363 | { |
364 | struct ifnet *ifp = m->m_pkthdr.rcvif; |
365 | struct nd_ifinfo *ndi = NULL; |
366 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
367 | struct nd_router_advert *nd_ra; |
368 | struct in6_addr saddr6 = ip6->ip6_src; |
369 | int mcast = 0; |
370 | union nd_opts ndopts; |
371 | struct nd_defrouter *dr = NULL; |
372 | u_int32_t mtu = 0; |
373 | char *lladdr = NULL; |
374 | u_int32_t lladdrlen = 0; |
375 | struct nd_prefix_list *nd_prefix_list_head = NULL; |
376 | u_int32_t nd_prefix_list_length = 0; |
377 | struct in6_ifaddr *ia6 = NULL; |
378 | struct nd_prefix_list *prfl; |
379 | struct nd_defrouter dr0; |
380 | u_int32_t advreachable; |
381 | |
382 | #if (DEVELOPMENT || DEBUG) |
383 | if (ip6_accept_rtadv == 0) |
384 | goto freeit; |
385 | #endif /* (DEVELOPMENT || DEBUG) */ |
386 | /* Expect 32-bit aligned data pointer on strict-align platforms */ |
387 | MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); |
388 | |
389 | /* |
390 | * Discard RA unless IFEF_ACCEPT_RTADV is set (as host), or when |
391 | * IFEF_IPV6_ROUTER is set (as router) but the RA is not locally |
392 | * generated. For convenience, we allow locally generated (rtadvd) |
393 | * RAs to be processed on the advertising interface, as a router. |
394 | * |
395 | * Note that we don't test against ip6_forwarding as we could be |
396 | * both a host and a router on different interfaces, hence the |
397 | * check against the per-interface flags. |
398 | */ |
399 | if (!(ifp->if_eflags & (IFEF_ACCEPT_RTADV | IFEF_IPV6_ROUTER)) || |
400 | ((ifp->if_eflags & IFEF_IPV6_ROUTER) && |
401 | (ia6 = ifa_foraddr6(&saddr6)) == NULL)) |
402 | goto freeit; |
403 | |
404 | if (ia6 != NULL) { |
405 | IFA_REMREF(&ia6->ia_ifa); |
406 | ia6 = NULL; |
407 | } |
408 | |
409 | if (ip6->ip6_hlim != 255) { |
410 | nd6log((LOG_ERR, |
411 | "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n" , |
412 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), |
413 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); |
414 | goto bad; |
415 | } |
416 | |
417 | if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { |
418 | nd6log((LOG_ERR, |
419 | "nd6_ra_input: src %s is not link-local\n" , |
420 | ip6_sprintf(&saddr6))); |
421 | goto bad; |
422 | } |
423 | |
424 | IP6_EXTHDR_CHECK(m, off, icmp6len, return); |
425 | nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); |
426 | |
427 | icmp6len -= sizeof (*nd_ra); |
428 | nd6_option_init(nd_ra + 1, icmp6len, &ndopts); |
429 | if (nd6_options(&ndopts) < 0) { |
430 | nd6log((LOG_INFO, |
431 | "nd6_ra_input: invalid ND option, ignored\n" )); |
432 | /* nd6_options have incremented stats */ |
433 | goto freeit; |
434 | } |
435 | |
436 | advreachable = nd_ra->nd_ra_reachable; |
437 | |
438 | /* remember if this is a multicasted advertisement */ |
439 | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) |
440 | mcast = 1; |
441 | |
442 | ndi = ND_IFINFO(ifp); |
443 | VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); |
444 | lck_mtx_lock(&ndi->lock); |
445 | bzero(&dr0, sizeof (dr0)); |
446 | dr0.rtaddr = saddr6; |
447 | dr0.flags = nd_ra->nd_ra_flags_reserved; |
448 | dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); |
449 | dr0.expire = net_uptime() + dr0.rtlifetime; |
450 | dr0.ifp = ifp; |
451 | /* unspecified or not? (RFC 2461 6.3.4) */ |
452 | if (advreachable) { |
453 | advreachable = ntohl(advreachable); |
454 | if (advreachable <= MAX_REACHABLE_TIME && |
455 | ndi->basereachable != advreachable) { |
456 | ndi->basereachable = advreachable; |
457 | ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); |
458 | ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ |
459 | } |
460 | } |
461 | if (nd_ra->nd_ra_retransmit) |
462 | ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); |
463 | if (nd_ra->nd_ra_curhoplimit) { |
464 | if (ndi->chlim < nd_ra->nd_ra_curhoplimit) { |
465 | ndi->chlim = nd_ra->nd_ra_curhoplimit; |
466 | } else if (ndi->chlim != nd_ra->nd_ra_curhoplimit) { |
467 | nd6log((LOG_ERR, |
468 | "RA with a lower CurHopLimit sent from " |
469 | "%s on %s (current = %d, received = %d). " |
470 | "Ignored.\n" , ip6_sprintf(&ip6->ip6_src), |
471 | if_name(ifp), ndi->chlim, |
472 | nd_ra->nd_ra_curhoplimit)); |
473 | } |
474 | } |
475 | lck_mtx_unlock(&ndi->lock); |
476 | lck_mtx_lock(nd6_mutex); |
477 | dr = defrtrlist_update(&dr0); |
478 | lck_mtx_unlock(nd6_mutex); |
479 | |
480 | /* |
481 | * prefix |
482 | */ |
483 | if (ndopts.nd_opts_pi) { |
484 | struct nd_opt_hdr *pt; |
485 | struct nd_opt_prefix_info *pi = NULL; |
486 | struct nd_prefix pr; |
487 | |
488 | for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; |
489 | pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; |
490 | pt = (struct nd_opt_hdr *)((caddr_t)pt + |
491 | (pt->nd_opt_len << 3))) { |
492 | struct in6_addr pi_mask; |
493 | bzero(&pi_mask, sizeof(pi_mask)); |
494 | |
495 | if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) |
496 | continue; |
497 | pi = (struct nd_opt_prefix_info *)pt; |
498 | |
499 | if (pi->nd_opt_pi_len != 4) { |
500 | nd6log((LOG_INFO, |
501 | "nd6_ra_input: invalid option " |
502 | "len %d for prefix information option, " |
503 | "ignored\n" , pi->nd_opt_pi_len)); |
504 | continue; |
505 | } |
506 | |
507 | if (128 < pi->nd_opt_pi_prefix_len) { |
508 | nd6log((LOG_INFO, |
509 | "nd6_ra_input: invalid prefix " |
510 | "len %d for prefix information option, " |
511 | "ignored\n" , pi->nd_opt_pi_prefix_len)); |
512 | continue; |
513 | } |
514 | |
515 | /* |
516 | * To ignore ::/64 make sure bits beyond prefixlen |
517 | * are set to zero |
518 | */ |
519 | in6_prefixlen2mask(&pi_mask, pi->nd_opt_pi_prefix_len); |
520 | pi->nd_opt_pi_prefix.s6_addr32[0] &= pi_mask.s6_addr32[0]; |
521 | pi->nd_opt_pi_prefix.s6_addr32[1] &= pi_mask.s6_addr32[1]; |
522 | pi->nd_opt_pi_prefix.s6_addr32[2] &= pi_mask.s6_addr32[2]; |
523 | pi->nd_opt_pi_prefix.s6_addr32[3] &= pi_mask.s6_addr32[3]; |
524 | |
525 | if (IN6_IS_ADDR_UNSPECIFIED(&pi->nd_opt_pi_prefix) || |
526 | IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || |
527 | IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { |
528 | nd6log((LOG_INFO, |
529 | "%s: invalid prefix %s, ignored\n" , |
530 | __func__, |
531 | ip6_sprintf(&pi->nd_opt_pi_prefix))); |
532 | continue; |
533 | } |
534 | |
535 | bzero(&pr, sizeof (pr)); |
536 | lck_mtx_init(&pr.ndpr_lock, ifa_mtx_grp, ifa_mtx_attr); |
537 | NDPR_LOCK(&pr); |
538 | pr.ndpr_prefix.sin6_family = AF_INET6; |
539 | pr.ndpr_prefix.sin6_len = sizeof (pr.ndpr_prefix); |
540 | pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; |
541 | pr.ndpr_ifp = m->m_pkthdr.rcvif; |
542 | |
543 | pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & |
544 | ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; |
545 | pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & |
546 | ND_OPT_PI_FLAG_AUTO) ? 1 : 0; |
547 | pr.ndpr_plen = pi->nd_opt_pi_prefix_len; |
548 | pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); |
549 | pr.ndpr_pltime = |
550 | ntohl(pi->nd_opt_pi_preferred_time); |
551 | |
552 | /* |
553 | * Exceptions to stateless autoconfiguration processing: |
554 | * + nd6_accept_6to4 == 0 && address has 6to4 prefix |
555 | * + ip6_only_allow_rfc4193_prefix != 0 && |
556 | * address not RFC 4193 |
557 | */ |
558 | if (ip6_only_allow_rfc4193_prefix && |
559 | !IN6_IS_ADDR_UNIQUE_LOCAL(&pi->nd_opt_pi_prefix)) { |
560 | nd6log((LOG_INFO, |
561 | "nd6_ra_input: no SLAAC on prefix %s " |
562 | "[not RFC 4193]\n" , |
563 | ip6_sprintf(&pi->nd_opt_pi_prefix))); |
564 | pr.ndpr_raf_auto = 0; |
565 | } else if (!nd6_accept_6to4 && |
566 | IN6_IS_ADDR_6TO4(&pi->nd_opt_pi_prefix)) { |
567 | nd6log((LOG_INFO, |
568 | "%s: no SLAAC on prefix %s " |
569 | "[6to4]\n" , __func__, |
570 | ip6_sprintf(&pi->nd_opt_pi_prefix))); |
571 | pr.ndpr_raf_auto = 0; |
572 | } |
573 | |
574 | if (in6_init_prefix_ltimes(&pr)) { |
575 | NDPR_UNLOCK(&pr); |
576 | lck_mtx_destroy(&pr.ndpr_lock, ifa_mtx_grp); |
577 | continue; /* prefix lifetime init failed */ |
578 | } else { |
579 | NDPR_UNLOCK(&pr); |
580 | } |
581 | (void) prelist_update(&pr, dr, m, mcast); |
582 | lck_mtx_destroy(&pr.ndpr_lock, ifa_mtx_grp); |
583 | |
584 | /* |
585 | * We have to copy the values out after the |
586 | * prelist_update call since some of these values won't |
587 | * be properly set until after the router advertisement |
588 | * updating can vet the values. |
589 | */ |
590 | prfl = NULL; |
591 | MALLOC(prfl, struct nd_prefix_list *, sizeof (*prfl), |
592 | M_TEMP, M_WAITOK | M_ZERO); |
593 | |
594 | if (prfl == NULL) { |
595 | log(LOG_DEBUG, "%s: unable to MALLOC RA prefix " |
596 | "structure\n" , __func__); |
597 | continue; |
598 | } |
599 | |
600 | /* this is only for nd6_post_msg(), otherwise unused */ |
601 | bcopy(&pr.ndpr_prefix, &prfl->pr.ndpr_prefix, |
602 | sizeof (prfl->pr.ndpr_prefix)); |
603 | prfl->pr.ndpr_raf = pr.ndpr_raf; |
604 | prfl->pr.ndpr_plen = pr.ndpr_plen; |
605 | prfl->pr.ndpr_vltime = pr.ndpr_vltime; |
606 | prfl->pr.ndpr_pltime = pr.ndpr_pltime; |
607 | prfl->pr.ndpr_expire = pr.ndpr_expire; |
608 | prfl->pr.ndpr_base_calendartime = |
609 | pr.ndpr_base_calendartime; |
610 | prfl->pr.ndpr_base_uptime = pr.ndpr_base_uptime; |
611 | prfl->pr.ndpr_stateflags = pr.ndpr_stateflags; |
612 | prfl->pr.ndpr_addrcnt = pr.ndpr_addrcnt; |
613 | prfl->pr.ndpr_ifp = pr.ndpr_ifp; |
614 | |
615 | prfl->next = nd_prefix_list_head; |
616 | nd_prefix_list_head = prfl; |
617 | nd_prefix_list_length++; |
618 | } |
619 | } |
620 | |
621 | /* |
622 | * MTU |
623 | */ |
624 | if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { |
625 | mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); |
626 | |
627 | /* lower bound */ |
628 | if (mtu < IPV6_MMTU) { |
629 | nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option " |
630 | "mtu=%d sent from %s, ignoring\n" , |
631 | mtu, ip6_sprintf(&ip6->ip6_src))); |
632 | goto skip; |
633 | } |
634 | |
635 | lck_mtx_lock(&ndi->lock); |
636 | /* upper bound */ |
637 | if (ndi->maxmtu) { |
638 | if (mtu <= ndi->maxmtu) { |
639 | int change = (ndi->linkmtu != mtu); |
640 | |
641 | ndi->linkmtu = mtu; |
642 | lck_mtx_unlock(&ndi->lock); |
643 | if (change) /* in6_maxmtu may change */ |
644 | in6_setmaxmtu(); |
645 | } else { |
646 | nd6log((LOG_INFO, "nd6_ra_input: bogus mtu " |
647 | "mtu=%d sent from %s; " |
648 | "exceeds maxmtu %d, ignoring\n" , |
649 | mtu, ip6_sprintf(&ip6->ip6_src), |
650 | ndi->maxmtu)); |
651 | lck_mtx_unlock(&ndi->lock); |
652 | } |
653 | } else { |
654 | lck_mtx_unlock(&ndi->lock); |
655 | nd6log((LOG_INFO, "nd6_ra_input: mtu option " |
656 | "mtu=%d sent from %s; maxmtu unknown, " |
657 | "ignoring\n" , |
658 | mtu, ip6_sprintf(&ip6->ip6_src))); |
659 | } |
660 | } |
661 | |
662 | skip: |
663 | |
664 | /* |
665 | * Source link layer address |
666 | */ |
667 | if (ndopts.nd_opts_src_lladdr) { |
668 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); |
669 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; |
670 | } |
671 | |
672 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
673 | nd6log((LOG_INFO, |
674 | "nd6_ra_input: lladdrlen mismatch for %s " |
675 | "(if %d, RA packet %d)\n" , |
676 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); |
677 | goto bad; |
678 | } |
679 | |
680 | if (dr && dr->stateflags & NDDRF_MAPPED) |
681 | saddr6 = dr->rtaddr_mapped; |
682 | |
683 | nd6_cache_lladdr(ifp, &saddr6, lladdr, (int)lladdrlen, |
684 | ND_ROUTER_ADVERT, 0); |
685 | |
686 | /* Post message */ |
687 | nd6_post_msg(KEV_ND6_RA, nd_prefix_list_head, nd_prefix_list_length, |
688 | mtu); |
689 | |
690 | /* |
691 | * Installing a link-layer address might change the state of the |
692 | * router's neighbor cache, which might also affect our on-link |
693 | * detection of adveritsed prefixes. |
694 | */ |
695 | lck_mtx_lock(nd6_mutex); |
696 | pfxlist_onlink_check(); |
697 | lck_mtx_unlock(nd6_mutex); |
698 | |
699 | freeit: |
700 | m_freem(m); |
701 | if (dr) |
702 | NDDR_REMREF(dr); |
703 | |
704 | prfl = NULL; |
705 | while ((prfl = nd_prefix_list_head) != NULL) { |
706 | nd_prefix_list_head = prfl->next; |
707 | FREE(prfl, M_TEMP); |
708 | } |
709 | |
710 | return; |
711 | |
712 | bad: |
713 | icmp6stat.icp6s_badra++; |
714 | goto freeit; |
715 | } |
716 | |
717 | /* |
718 | * default router list proccessing sub routines |
719 | */ |
720 | |
721 | /* tell the change to user processes watching the routing socket. */ |
722 | static void |
723 | nd6_rtmsg(int cmd, struct rtentry *rt) |
724 | { |
725 | struct rt_addrinfo info; |
726 | struct ifnet *ifp = rt->rt_ifp; |
727 | |
728 | RT_LOCK_ASSERT_HELD(rt); |
729 | |
730 | bzero((caddr_t)&info, sizeof (info)); |
731 | /* It's not necessary to lock ifp for if_lladdr */ |
732 | info.rti_info[RTAX_DST] = rt_key(rt); |
733 | info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; |
734 | info.rti_info[RTAX_NETMASK] = rt_mask(rt); |
735 | /* |
736 | * ifa_addr pointers for both should always be valid |
737 | * in this context; no need to hold locks. |
738 | */ |
739 | info.rti_info[RTAX_IFP] = ifp->if_lladdr->ifa_addr; |
740 | info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; |
741 | |
742 | rt_missmsg(cmd, &info, rt->rt_flags, 0); |
743 | } |
744 | |
745 | static void |
746 | defrouter_addreq(struct nd_defrouter *new, boolean_t scoped) |
747 | { |
748 | struct sockaddr_in6 def, mask, gate; |
749 | struct rtentry *newrt = NULL; |
750 | unsigned int ifscope; |
751 | int err; |
752 | struct nd_ifinfo *ndi = ND_IFINFO(new->ifp); |
753 | |
754 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); |
755 | NDDR_LOCK_ASSERT_NOTHELD(new); |
756 | /* |
757 | * We're free to lock and unlock NDDR because our callers |
758 | * are holding an extra reference for us. |
759 | */ |
760 | |
761 | NDDR_LOCK(new); |
762 | if (new->stateflags & NDDRF_INSTALLED) |
763 | goto out; |
764 | |
765 | if (new->ifp->if_eflags & IFEF_IPV6_ROUTER) { |
766 | nd6log2((LOG_INFO, "%s: ignoring router %s, scoped=%d, " |
767 | "static=%d on advertising interface\n" , if_name(new->ifp), |
768 | ip6_sprintf(&new->rtaddr), scoped, |
769 | (new->stateflags & NDDRF_STATIC) ? 1 : 0)); |
770 | goto out; |
771 | } |
772 | |
773 | nd6log2((LOG_INFO, "%s: adding default router %s, scoped=%d, " |
774 | "static=%d\n" , if_name(new->ifp), ip6_sprintf(&new->rtaddr), |
775 | scoped, (new->stateflags & NDDRF_STATIC) ? 1 : 0)); |
776 | |
777 | Bzero(&def, sizeof (def)); |
778 | Bzero(&mask, sizeof (mask)); |
779 | Bzero(&gate, sizeof (gate)); |
780 | |
781 | def.sin6_len = mask.sin6_len = gate.sin6_len |
782 | = sizeof (struct sockaddr_in6); |
783 | def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; |
784 | |
785 | if (new->stateflags & NDDRF_MAPPED) |
786 | gate.sin6_addr = new->rtaddr_mapped; |
787 | else |
788 | gate.sin6_addr = new->rtaddr; |
789 | |
790 | ifscope = scoped ? new->ifp->if_index : IFSCOPE_NONE; |
791 | NDDR_UNLOCK(new); |
792 | |
793 | /* |
794 | * Cellular networks may have buggy deployments |
795 | * with gateway IPv6 link local address with same |
796 | * interface identifier as the one that has been |
797 | * assigned for the cellular context. |
798 | * If gateway is same as locally configured link local |
799 | * interface on cellular interface, generated a different one |
800 | * and store it in the nd_defrouter entry and use it to work |
801 | * on routing table |
802 | */ |
803 | if (new->ifp->if_type == IFT_CELLULAR && |
804 | !(new->stateflags & NDDRF_STATIC) && |
805 | !(new->stateflags & NDDRF_MAPPED) && |
806 | IN6_IS_ADDR_LINKLOCAL(&gate.sin6_addr) && |
807 | ndi && !(ndi->flags & ND6_IFF_PERFORMNUD)) { |
808 | struct in6_ifaddr *tmp_ia6 = in6ifa_ifpforlinklocal(new->ifp, 0); |
809 | |
810 | if (tmp_ia6 != NULL && |
811 | !(tmp_ia6->ia6_flags & IN6_IFF_NOTMANUAL) && |
812 | IN6_ARE_ADDR_EQUAL(&tmp_ia6->ia_addr.sin6_addr, |
813 | &gate.sin6_addr)) { |
814 | gate.sin6_addr.s6_addr8[15] += 1; |
815 | new->rtaddr_mapped = gate.sin6_addr; |
816 | new->stateflags |= NDDRF_MAPPED; |
817 | |
818 | nd6log((LOG_INFO, "%s: Default router %s mapped " |
819 | "to " , if_name(new->ifp), ip6_sprintf(&new->rtaddr))); |
820 | nd6log((LOG_INFO, "%s\n" , ip6_sprintf(&new->rtaddr_mapped))); |
821 | } |
822 | } |
823 | |
824 | err = rtrequest_scoped(RTM_ADD, (struct sockaddr *)&def, |
825 | (struct sockaddr *)&gate, (struct sockaddr *)&mask, |
826 | RTF_GATEWAY, &newrt, ifscope); |
827 | |
828 | if (newrt) { |
829 | RT_LOCK(newrt); |
830 | nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ |
831 | RT_REMREF_LOCKED(newrt); |
832 | RT_UNLOCK(newrt); |
833 | NDDR_LOCK(new); |
834 | new->stateflags |= NDDRF_INSTALLED; |
835 | if (ifscope != IFSCOPE_NONE) |
836 | new->stateflags |= NDDRF_IFSCOPE; |
837 | } else { |
838 | nd6log((LOG_ERR, "%s: failed to add default router " |
839 | "%s on %s scoped %d (errno = %d)\n" , __func__, |
840 | ip6_sprintf(&gate.sin6_addr), if_name(new->ifp), |
841 | (ifscope != IFSCOPE_NONE), err)); |
842 | NDDR_LOCK(new); |
843 | } |
844 | new->err = err; |
845 | |
846 | out: |
847 | NDDR_UNLOCK(new); |
848 | } |
849 | |
850 | struct nd_defrouter * |
851 | defrouter_lookup( |
852 | struct in6_addr *addr, |
853 | struct ifnet *ifp) |
854 | { |
855 | struct nd_defrouter *dr; |
856 | |
857 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
858 | |
859 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
860 | dr = TAILQ_NEXT(dr, dr_entry)) { |
861 | NDDR_LOCK(dr); |
862 | if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) { |
863 | NDDR_ADDREF_LOCKED(dr); |
864 | NDDR_UNLOCK(dr); |
865 | return (dr); |
866 | } |
867 | NDDR_UNLOCK(dr); |
868 | } |
869 | |
870 | return (NULL); /* search failed */ |
871 | } |
872 | |
873 | /* |
874 | * Remove the default route for a given router. |
875 | * This is just a subroutine function for defrouter_select(), and should |
876 | * not be called from anywhere else. |
877 | */ |
878 | static void |
879 | defrouter_delreq(struct nd_defrouter *dr) |
880 | { |
881 | struct sockaddr_in6 def, mask, gate; |
882 | struct rtentry *oldrt = NULL; |
883 | unsigned int ifscope; |
884 | int err; |
885 | |
886 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); |
887 | NDDR_LOCK_ASSERT_NOTHELD(dr); |
888 | /* |
889 | * We're free to lock and unlock NDDR because our callers |
890 | * are holding an extra reference for us. |
891 | */ |
892 | NDDR_LOCK(dr); |
893 | /* ifp would be NULL for the "drany" case */ |
894 | if (dr->ifp != NULL && !(dr->stateflags & NDDRF_INSTALLED)) |
895 | goto out; |
896 | |
897 | nd6log2((LOG_INFO, "%s: removing default router %s, scoped=%d, " |
898 | "static=%d\n" , dr->ifp != NULL ? if_name(dr->ifp) : "ANY" , |
899 | ip6_sprintf(&dr->rtaddr), (dr->stateflags & NDDRF_IFSCOPE) ? 1 : 0, |
900 | (dr->stateflags & NDDRF_STATIC) ? 1 : 0)); |
901 | |
902 | Bzero(&def, sizeof (def)); |
903 | Bzero(&mask, sizeof (mask)); |
904 | Bzero(&gate, sizeof (gate)); |
905 | |
906 | def.sin6_len = mask.sin6_len = gate.sin6_len |
907 | = sizeof (struct sockaddr_in6); |
908 | def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; |
909 | |
910 | /* |
911 | * The router entry may be mapped to a different address. |
912 | * If that is the case, use the mapped address as gateway |
913 | * to do operation on the routing table. |
914 | * To get more context, read the related comment in |
915 | * defrouter_addreq |
916 | */ |
917 | if (dr->stateflags & NDDRF_MAPPED) |
918 | gate.sin6_addr = dr->rtaddr_mapped; |
919 | else |
920 | gate.sin6_addr = dr->rtaddr; |
921 | |
922 | if (dr->ifp != NULL) { |
923 | ifscope = (dr->stateflags & NDDRF_IFSCOPE) ? |
924 | dr->ifp->if_index : IFSCOPE_NONE; |
925 | } else { |
926 | ifscope = IFSCOPE_NONE; |
927 | } |
928 | NDDR_UNLOCK(dr); |
929 | |
930 | err = rtrequest_scoped(RTM_DELETE, |
931 | (struct sockaddr *)&def, (struct sockaddr *)&gate, |
932 | (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt, ifscope); |
933 | |
934 | if (oldrt) { |
935 | RT_LOCK(oldrt); |
936 | nd6_rtmsg(RTM_DELETE, oldrt); |
937 | RT_UNLOCK(oldrt); |
938 | rtfree(oldrt); |
939 | } else if (err != ESRCH) { |
940 | nd6log((LOG_ERR, "%s: failed to delete default router " |
941 | "%s on %s scoped %d (errno = %d)\n" , __func__, |
942 | ip6_sprintf(&gate.sin6_addr), dr->ifp != NULL ? |
943 | if_name(dr->ifp) : "ANY" , (ifscope != IFSCOPE_NONE), err)); |
944 | } |
945 | NDDR_LOCK(dr); |
946 | /* ESRCH means it's no longer in the routing table; ignore it */ |
947 | if (oldrt != NULL || err == ESRCH) { |
948 | dr->stateflags &= ~NDDRF_INSTALLED; |
949 | if (ifscope != IFSCOPE_NONE) |
950 | dr->stateflags &= ~NDDRF_IFSCOPE; |
951 | } |
952 | dr->err = 0; |
953 | out: |
954 | NDDR_UNLOCK(dr); |
955 | } |
956 | |
957 | |
958 | /* |
959 | * remove all default routes from default router list |
960 | */ |
961 | void |
962 | defrouter_reset(void) |
963 | { |
964 | struct nd_defrouter *dr, drany; |
965 | |
966 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
967 | |
968 | dr = TAILQ_FIRST(&nd_defrouter); |
969 | while (dr) { |
970 | NDDR_LOCK(dr); |
971 | if (dr->stateflags & NDDRF_INSTALLED) { |
972 | NDDR_ADDREF_LOCKED(dr); |
973 | NDDR_UNLOCK(dr); |
974 | lck_mtx_unlock(nd6_mutex); |
975 | defrouter_delreq(dr); |
976 | lck_mtx_lock(nd6_mutex); |
977 | NDDR_REMREF(dr); |
978 | dr = TAILQ_FIRST(&nd_defrouter); |
979 | } else { |
980 | NDDR_UNLOCK(dr); |
981 | dr = TAILQ_NEXT(dr, dr_entry); |
982 | } |
983 | } |
984 | |
985 | /* Nuke primary (non-scoped) default router */ |
986 | bzero(&drany, sizeof (drany)); |
987 | lck_mtx_init(&drany.nddr_lock, ifa_mtx_grp, ifa_mtx_attr); |
988 | lck_mtx_unlock(nd6_mutex); |
989 | defrouter_delreq(&drany); |
990 | lck_mtx_destroy(&drany.nddr_lock, ifa_mtx_grp); |
991 | lck_mtx_lock(nd6_mutex); |
992 | } |
993 | |
994 | int |
995 | defrtrlist_ioctl(u_long cmd, caddr_t data) |
996 | { |
997 | struct nd_defrouter dr0; |
998 | unsigned int ifindex; |
999 | struct ifnet *dr_ifp; |
1000 | int error = 0, add = 0; |
1001 | |
1002 | /* XXX Handle mapped default router entries */ |
1003 | switch (cmd) { |
1004 | case SIOCDRADD_IN6_32: /* struct in6_defrouter_32 */ |
1005 | case SIOCDRADD_IN6_64: /* struct in6_defrouter_64 */ |
1006 | ++add; |
1007 | /* FALLTHRU */ |
1008 | case SIOCDRDEL_IN6_32: /* struct in6_defrouter_32 */ |
1009 | case SIOCDRDEL_IN6_64: /* struct in6_defrouter_64 */ |
1010 | bzero(&dr0, sizeof (dr0)); |
1011 | if (cmd == SIOCDRADD_IN6_64 || cmd == SIOCDRDEL_IN6_64) { |
1012 | struct in6_defrouter_64 *r_64 = |
1013 | (struct in6_defrouter_64 *)(void *)data; |
1014 | u_int16_t i; |
1015 | |
1016 | bcopy(&r_64->rtaddr.sin6_addr, &dr0.rtaddr, |
1017 | sizeof (dr0.rtaddr)); |
1018 | dr0.flags = r_64->flags; |
1019 | bcopy(&r_64->if_index, &i, sizeof (i)); |
1020 | ifindex = i; |
1021 | } else { |
1022 | struct in6_defrouter_32 *r_32 = |
1023 | (struct in6_defrouter_32 *)(void *)data; |
1024 | u_int16_t i; |
1025 | |
1026 | bcopy(&r_32->rtaddr.sin6_addr, &dr0.rtaddr, |
1027 | sizeof (dr0.rtaddr)); |
1028 | dr0.flags = r_32->flags; |
1029 | bcopy(&r_32->if_index, &i, sizeof (i)); |
1030 | ifindex = i; |
1031 | } |
1032 | ifnet_head_lock_shared(); |
1033 | /* Don't need to check is ifindex is < 0 since it's unsigned */ |
1034 | if (if_index < ifindex || |
1035 | (dr_ifp = ifindex2ifnet[ifindex]) == NULL) { |
1036 | ifnet_head_done(); |
1037 | error = EINVAL; |
1038 | break; |
1039 | } |
1040 | dr0.ifp = dr_ifp; |
1041 | ifnet_head_done(); |
1042 | |
1043 | if (ND_IFINFO(dr_ifp) == NULL || |
1044 | !ND_IFINFO(dr_ifp)->initialized) { |
1045 | error = ENXIO; |
1046 | break; |
1047 | } |
1048 | |
1049 | if (IN6_IS_SCOPE_EMBED(&dr0.rtaddr)) { |
1050 | uint16_t *scope = &dr0.rtaddr.s6_addr16[1]; |
1051 | |
1052 | if (*scope == 0) { |
1053 | *scope = htons(dr_ifp->if_index); |
1054 | } else if (*scope != htons(dr_ifp->if_index)) { |
1055 | error = EINVAL; |
1056 | break; |
1057 | } |
1058 | } |
1059 | |
1060 | if (add) |
1061 | error = defrtrlist_add_static(&dr0); |
1062 | if (!add || error != 0) { |
1063 | int err = defrtrlist_del_static(&dr0); |
1064 | if (!add) |
1065 | error = err; |
1066 | } |
1067 | break; |
1068 | |
1069 | default: |
1070 | error = EOPNOTSUPP; /* check for safety */ |
1071 | break; |
1072 | } |
1073 | |
1074 | return (error); |
1075 | } |
1076 | |
1077 | /* |
1078 | * XXX Please make sure to remove dr from the |
1079 | * global default router tailq list before this |
1080 | * function call. |
1081 | * Also ensure that you release the list reference |
1082 | * only after calling this routine. |
1083 | */ |
1084 | void |
1085 | defrtrlist_del(struct nd_defrouter *dr) |
1086 | { |
1087 | #if (DEVELOPMENT || DEBUG) |
1088 | struct nd_defrouter *dr_itr = NULL; |
1089 | #endif |
1090 | struct nd_prefix *pr; |
1091 | struct ifnet *ifp = dr->ifp; |
1092 | struct nd_ifinfo *ndi = NULL; |
1093 | boolean_t resetmtu; |
1094 | |
1095 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1096 | |
1097 | #if (DEVELOPMENT || DEBUG) |
1098 | /* |
1099 | * Verify that the router is not in the global default |
1100 | * router list. |
1101 | * Can't use defrouter_lookup here because that just works |
1102 | * with address and ifp pointer. |
1103 | * We have to compare the memory here. |
1104 | * Also we can't use ASSERT here as that is not defined |
1105 | * for development builds. |
1106 | */ |
1107 | TAILQ_FOREACH(dr_itr, &nd_defrouter, dr_entry) |
1108 | VERIFY(dr != dr_itr); |
1109 | #endif |
1110 | ++nd6_defrouter_genid; |
1111 | /* |
1112 | * Flush all the routing table entries that use the router |
1113 | * as a next hop. |
1114 | */ |
1115 | /* above is a good condition? */ |
1116 | NDDR_ADDREF(dr); |
1117 | lck_mtx_unlock(nd6_mutex); |
1118 | if (dr->stateflags & NDDRF_MAPPED) |
1119 | rt6_flush(&dr->rtaddr_mapped, ifp); |
1120 | else |
1121 | rt6_flush(&dr->rtaddr, ifp); |
1122 | |
1123 | lck_mtx_lock(nd6_mutex); |
1124 | NDDR_REMREF(dr); |
1125 | nd6log2((LOG_INFO, "%s: freeing defrouter %s\n" , if_name(dr->ifp), |
1126 | ip6_sprintf(&dr->rtaddr))); |
1127 | /* |
1128 | * Delete it from the routing table. |
1129 | */ |
1130 | NDDR_ADDREF(dr); |
1131 | lck_mtx_unlock(nd6_mutex); |
1132 | defrouter_delreq(dr); |
1133 | lck_mtx_lock(nd6_mutex); |
1134 | NDDR_REMREF(dr); |
1135 | |
1136 | /* |
1137 | * Also delete all the pointers to the router in each prefix lists. |
1138 | */ |
1139 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { |
1140 | struct nd_pfxrouter *pfxrtr; |
1141 | |
1142 | NDPR_LOCK(pr); |
1143 | if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) |
1144 | pfxrtr_del(pfxrtr, pr); |
1145 | NDPR_UNLOCK(pr); |
1146 | } |
1147 | |
1148 | pfxlist_onlink_check(); |
1149 | |
1150 | resetmtu = FALSE; |
1151 | ndi = ND_IFINFO(ifp); |
1152 | VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); |
1153 | lck_mtx_lock(&ndi->lock); |
1154 | VERIFY(ndi->ndefrouters >= 0); |
1155 | if (ndi->ndefrouters > 0 && --ndi->ndefrouters == 0) { |
1156 | nd6_ifreset(ifp); |
1157 | resetmtu = TRUE; |
1158 | } |
1159 | lck_mtx_unlock(&ndi->lock); |
1160 | |
1161 | /* |
1162 | * If the router is the primary one, choose a new one. |
1163 | * We always try to pick another eligible router |
1164 | * on this interface as we do scoped routing |
1165 | */ |
1166 | defrouter_select(ifp); |
1167 | |
1168 | if (resetmtu) |
1169 | nd6_setmtu(ifp); |
1170 | } |
1171 | |
1172 | int |
1173 | defrtrlist_add_static(struct nd_defrouter *new) |
1174 | { |
1175 | struct nd_defrouter *dr; |
1176 | int err = 0; |
1177 | |
1178 | new->rtlifetime = -1; |
1179 | new->stateflags |= NDDRF_STATIC; |
1180 | |
1181 | /* we only want the preference level */ |
1182 | new->flags &= ND_RA_FLAG_RTPREF_MASK; |
1183 | |
1184 | lck_mtx_lock(nd6_mutex); |
1185 | dr = defrouter_lookup(&new->rtaddr, new->ifp); |
1186 | if (dr != NULL && !(dr->stateflags & NDDRF_STATIC)) { |
1187 | err = EINVAL; |
1188 | } else { |
1189 | if (dr != NULL) |
1190 | NDDR_REMREF(dr); |
1191 | dr = defrtrlist_update(new); |
1192 | if (dr != NULL) |
1193 | err = dr->err; |
1194 | else |
1195 | err = ENOMEM; |
1196 | } |
1197 | if (dr != NULL) |
1198 | NDDR_REMREF(dr); |
1199 | lck_mtx_unlock(nd6_mutex); |
1200 | |
1201 | return (err); |
1202 | } |
1203 | |
1204 | int |
1205 | defrtrlist_del_static(struct nd_defrouter *new) |
1206 | { |
1207 | struct nd_defrouter *dr; |
1208 | |
1209 | lck_mtx_lock(nd6_mutex); |
1210 | dr = defrouter_lookup(&new->rtaddr, new->ifp); |
1211 | if (dr == NULL || !(dr->stateflags & NDDRF_STATIC)) { |
1212 | if (dr != NULL) |
1213 | NDDR_REMREF(dr); |
1214 | dr = NULL; |
1215 | } else { |
1216 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); |
1217 | defrtrlist_del(dr); |
1218 | NDDR_REMREF(dr); /* remove list reference */ |
1219 | NDDR_REMREF(dr); |
1220 | } |
1221 | lck_mtx_unlock(nd6_mutex); |
1222 | |
1223 | return (dr != NULL ? 0 : EINVAL); |
1224 | } |
1225 | |
1226 | /* |
1227 | * for default router selection |
1228 | * regards router-preference field as a 2-bit signed integer |
1229 | */ |
1230 | static int |
1231 | rtpref(struct nd_defrouter *dr) |
1232 | { |
1233 | switch (dr->flags & ND_RA_FLAG_RTPREF_MASK) { |
1234 | case ND_RA_FLAG_RTPREF_HIGH: |
1235 | return (RTPREF_HIGH); |
1236 | case ND_RA_FLAG_RTPREF_MEDIUM: |
1237 | case ND_RA_FLAG_RTPREF_RSV: |
1238 | return (RTPREF_MEDIUM); |
1239 | case ND_RA_FLAG_RTPREF_LOW: |
1240 | return (RTPREF_LOW); |
1241 | default: |
1242 | /* |
1243 | * This case should never happen. If it did, it would mean a |
1244 | * serious bug of kernel internal. We thus always bark here. |
1245 | * Or, can we even panic? |
1246 | */ |
1247 | log(LOG_ERR, "rtpref: impossible RA flag %x\n" , dr->flags); |
1248 | return (RTPREF_INVALID); |
1249 | } |
1250 | /* NOTREACHED */ |
1251 | } |
1252 | |
1253 | /* |
1254 | * Default Router Selection according to Section 6.3.6 of RFC 2461 and RFC 4191: |
1255 | * |
1256 | * 1) Routers that are reachable or probably reachable should be preferred. |
1257 | * If we have more than one (probably) reachable router, prefer ones |
1258 | * with the highest router preference. |
1259 | * 2) When no routers on the list are known to be reachable or |
1260 | * probably reachable, routers SHOULD be selected in a round-robin |
1261 | * fashion, regardless of router preference values. |
1262 | * 3) If the Default Router List is empty, assume that all |
1263 | * destinations are on-link. |
1264 | * |
1265 | * When Scoped Routing is enabled, the selection logic is amended as follows: |
1266 | * |
1267 | * a) When a default interface is specified, the primary/non-scoped default |
1268 | * router will be set to the reachable router on that link (if any) with |
1269 | * the highest router preference. |
1270 | * b) When there are more than one routers on the same link, the one with |
1271 | * the highest router preference will be installed, either as scoped or |
1272 | * non-scoped route entry. If they all share the same preference value, |
1273 | * the one installed will be the static or the first encountered reachable |
1274 | * router, i.e. static one wins over dynamic. |
1275 | * c) When no routers on the list are known to be reachable, or probably |
1276 | * reachable, no round-robin selection will take place when the default |
1277 | * interface is set. |
1278 | * |
1279 | * We assume nd_defrouter is sorted by router preference value. |
1280 | * Since the code below covers both with and without router preference cases, |
1281 | * we do not need to classify the cases by ifdef. |
1282 | */ |
1283 | void |
1284 | defrouter_select(struct ifnet *ifp) |
1285 | { |
1286 | struct nd_defrouter *dr = NULL; |
1287 | struct nd_defrouter *selected_dr = NULL; |
1288 | struct nd_defrouter *installed_dr = NULL; |
1289 | struct llinfo_nd6 *ln = NULL; |
1290 | struct rtentry *rt = NULL; |
1291 | struct nd_ifinfo *ndi = NULL; |
1292 | unsigned int genid = 0; |
1293 | boolean_t is_installed_reachable = FALSE; |
1294 | |
1295 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1296 | |
1297 | if (ifp == NULL) { |
1298 | nd6log2((LOG_INFO, |
1299 | "%s:%d: Return early. NULL interface" , |
1300 | __func__, __LINE__)); |
1301 | return; |
1302 | } |
1303 | |
1304 | if (ifp == lo_ifp) { |
1305 | nd6log2((LOG_INFO, |
1306 | "%s:%d: Return early. " |
1307 | "Default router select called for loopback.\n" , |
1308 | __func__, __LINE__)); |
1309 | return; |
1310 | } |
1311 | |
1312 | if (ifp->if_eflags & IFEF_IPV6_ROUTER) { |
1313 | nd6log2((LOG_INFO, |
1314 | "%s:%d: Return early. " |
1315 | "Default router select called for interface" |
1316 | " %s with IFEF_IPV6_ROUTER flag set\n" , |
1317 | __func__, __LINE__, if_name(ifp))); |
1318 | return; |
1319 | } |
1320 | |
1321 | /* |
1322 | * Let's handle easy case (3) first: |
1323 | * If default router list is empty, there's nothing to be done. |
1324 | */ |
1325 | if (!TAILQ_FIRST(&nd_defrouter)) { |
1326 | nd6log2((LOG_INFO, |
1327 | "%s:%d: Return early. " |
1328 | "Default router is empty.\n" , __func__, __LINE__)); |
1329 | return; |
1330 | } |
1331 | |
1332 | /* |
1333 | * Take an early exit if number of routers in nd_ifinfo is |
1334 | * 0 for the interface. |
1335 | */ |
1336 | ndi = ND_IFINFO(ifp); |
1337 | if (!ndi || !ndi->initialized) { |
1338 | nd6log2((LOG_INFO, |
1339 | "%s:%d: Return early. " |
1340 | "Interface %s's nd_ifinfo not initialized.\n" , |
1341 | __func__, __LINE__, if_name(ifp))); |
1342 | return; |
1343 | } |
1344 | |
1345 | if (ndi->ndefrouters == 0) { |
1346 | nd6log2((LOG_INFO, |
1347 | "%s:%d: Return early. " |
1348 | "%s does not have any default routers.\n" , |
1349 | __func__, __LINE__, if_name(ifp))); |
1350 | return; |
1351 | } |
1352 | |
1353 | /* |
1354 | * Due to the number of times we drop nd6_mutex, we need to |
1355 | * serialize this function. |
1356 | */ |
1357 | while (nd_defrouter_busy) { |
1358 | nd_defrouter_waiters++; |
1359 | msleep(nd_defrouter_waitchan, nd6_mutex, (PZERO-1), |
1360 | __func__, NULL); |
1361 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1362 | } |
1363 | nd_defrouter_busy = TRUE; |
1364 | |
1365 | /* |
1366 | * Search for a (probably) reachable router from the list. |
1367 | * We just pick up the first reachable one (if any), assuming that |
1368 | * the ordering rule of the list described in defrtrlist_update(). |
1369 | * |
1370 | * For all intents and purposes of Scoped Routing: |
1371 | * selected_dr = candidate for primary router |
1372 | * installed_dr = currently installed primary router |
1373 | */ |
1374 | genid = nd6_defrouter_genid; |
1375 | dr = TAILQ_FIRST(&nd_defrouter); |
1376 | |
1377 | while (dr != NULL) { |
1378 | struct in6_addr rtaddr; |
1379 | struct ifnet *drifp = NULL; |
1380 | struct nd_defrouter *drrele = NULL; |
1381 | |
1382 | NDDR_LOCK(dr); |
1383 | drifp = dr->ifp; |
1384 | if (drifp != ifp) { |
1385 | NDDR_UNLOCK(dr); |
1386 | dr = TAILQ_NEXT(dr, dr_entry); |
1387 | continue; |
1388 | } |
1389 | |
1390 | /* |
1391 | * Optimize for the common case. |
1392 | * When the interface has only one default router |
1393 | * there's no point checking for reachability as |
1394 | * there's nothing else to choose from. |
1395 | */ |
1396 | if (ndi->ndefrouters == 1) { |
1397 | nd6log2((LOG_INFO, |
1398 | "%s:%d: Fast forward default router selection " |
1399 | "as interface %s has learned only one default " |
1400 | "router and there's nothing else to choose from.\n" , |
1401 | __func__, __LINE__, if_name(ifp))); |
1402 | VERIFY(selected_dr == NULL && installed_dr == NULL); |
1403 | selected_dr = dr; |
1404 | if (dr->stateflags & NDDRF_INSTALLED) |
1405 | installed_dr = dr; |
1406 | NDDR_ADDREF_LOCKED(selected_dr); |
1407 | NDDR_UNLOCK(dr); |
1408 | goto install_route; |
1409 | } |
1410 | |
1411 | if (dr->stateflags & NDDRF_MAPPED) |
1412 | rtaddr = dr->rtaddr_mapped; |
1413 | else |
1414 | rtaddr = dr->rtaddr; |
1415 | |
1416 | NDDR_ADDREF_LOCKED(dr); /* for this for loop */ |
1417 | NDDR_UNLOCK(dr); |
1418 | |
1419 | /* Callee returns a locked route upon success */ |
1420 | if (selected_dr == NULL) { |
1421 | lck_mtx_unlock(nd6_mutex); |
1422 | if ((rt = nd6_lookup(&rtaddr, 0, drifp, 0)) != NULL && |
1423 | (ln = rt->rt_llinfo) != NULL && |
1424 | ND6_IS_LLINFO_PROBREACH(ln)) { |
1425 | RT_LOCK_ASSERT_HELD(rt); |
1426 | selected_dr = dr; |
1427 | NDDR_ADDREF(selected_dr); |
1428 | } |
1429 | lck_mtx_lock(nd6_mutex); |
1430 | } |
1431 | |
1432 | if (rt) { |
1433 | RT_REMREF_LOCKED(rt); |
1434 | RT_UNLOCK(rt); |
1435 | rt = NULL; |
1436 | } |
1437 | |
1438 | /* |
1439 | * Handle case (b) |
1440 | * When there are more than one routers on the same link, the one with |
1441 | * the highest router preference will be installed. |
1442 | * Since the list is in decreasing order of preference: |
1443 | * 1) If selected_dr is not NULL, only use dr if it is static and has |
1444 | * equal preference and selected_dr is not static. |
1445 | * 2) Else if selected_dr is NULL, and dr is static make selected_dr = dr |
1446 | */ |
1447 | NDDR_LOCK(dr); |
1448 | if (((selected_dr && (rtpref(dr) >= rtpref(selected_dr)) && |
1449 | !(selected_dr->stateflags & NDDRF_STATIC)) || |
1450 | (selected_dr == NULL)) && |
1451 | (dr->stateflags & NDDRF_STATIC)) { |
1452 | if (selected_dr) { |
1453 | /* Release it later on */ |
1454 | VERIFY(drrele == NULL); |
1455 | drrele = selected_dr; |
1456 | } |
1457 | selected_dr = dr; |
1458 | NDDR_ADDREF_LOCKED(selected_dr); |
1459 | } |
1460 | |
1461 | /* Record the currently installed router */ |
1462 | if (dr->stateflags & NDDRF_INSTALLED) { |
1463 | if (installed_dr == NULL) { |
1464 | installed_dr = dr; |
1465 | NDDR_ADDREF_LOCKED(installed_dr); |
1466 | if (dr->stateflags & NDDRF_MAPPED) |
1467 | rtaddr = installed_dr->rtaddr_mapped; |
1468 | else |
1469 | rtaddr = installed_dr->rtaddr; |
1470 | NDDR_UNLOCK(dr); |
1471 | lck_mtx_unlock(nd6_mutex); |
1472 | /* Callee returns a locked route upon success */ |
1473 | if ((rt = nd6_lookup(&rtaddr, 0, ifp, 0)) != NULL) { |
1474 | RT_LOCK_ASSERT_HELD(rt); |
1475 | if ((ln = rt->rt_llinfo) != NULL && |
1476 | ND6_IS_LLINFO_PROBREACH(ln)) |
1477 | is_installed_reachable = TRUE; |
1478 | |
1479 | RT_REMREF_LOCKED(rt); |
1480 | RT_UNLOCK(rt); |
1481 | rt = NULL; |
1482 | } |
1483 | lck_mtx_lock(nd6_mutex); |
1484 | } else { |
1485 | /* this should not happen; warn for diagnosis */ |
1486 | nd6log((LOG_ERR, "defrouter_select: more than one " |
1487 | "default router is installed for interface :%s.\n" , |
1488 | if_name(ifp))); |
1489 | NDDR_UNLOCK(dr); |
1490 | } |
1491 | } else |
1492 | NDDR_UNLOCK(dr); |
1493 | |
1494 | NDDR_REMREF(dr); /* for this for loop */ |
1495 | if (drrele != NULL) |
1496 | NDDR_REMREF(drrele); |
1497 | |
1498 | /* |
1499 | * Check if the list changed when we gave up |
1500 | * the nd6_mutex lock |
1501 | */ |
1502 | if(genid != nd6_defrouter_genid) { |
1503 | if (selected_dr) { |
1504 | NDDR_REMREF(selected_dr); |
1505 | selected_dr = NULL; |
1506 | } |
1507 | |
1508 | if (installed_dr) { |
1509 | NDDR_REMREF(installed_dr); |
1510 | installed_dr = NULL; |
1511 | } |
1512 | |
1513 | if (ndi->ndefrouters == 0) { |
1514 | nd6log2((LOG_INFO, |
1515 | "%s:%d: Interface %s no longer " |
1516 | "has any default routers. Abort.\n" , |
1517 | __func__, __LINE__, if_name(ifp))); |
1518 | goto out; |
1519 | } |
1520 | nd6log2((LOG_INFO, |
1521 | "%s:%d: Iterate default router list again " |
1522 | "for interface %s, as the list seems to have " |
1523 | "changed during release-reaquire of global " |
1524 | "nd6_mutex lock.\n" , |
1525 | __func__, __LINE__, if_name(ifp))); |
1526 | |
1527 | is_installed_reachable = FALSE; |
1528 | genid = nd6_defrouter_genid; |
1529 | dr = TAILQ_FIRST(&nd_defrouter); |
1530 | } else { |
1531 | dr = TAILQ_NEXT(dr, dr_entry); |
1532 | } |
1533 | } |
1534 | |
1535 | /* |
1536 | * If none of the default routers was found to be reachable, |
1537 | * round-robin the list regardless of preference. |
1538 | * Please note selected_dr equal to NULL implies that even |
1539 | * installed default router is not reachable |
1540 | */ |
1541 | if (selected_dr == NULL) { |
1542 | if (installed_dr) { |
1543 | for (dr = TAILQ_NEXT(installed_dr, dr_entry); dr; |
1544 | dr = TAILQ_NEXT(dr, dr_entry)) { |
1545 | if (installed_dr->ifp != dr->ifp) |
1546 | continue; |
1547 | selected_dr = dr; |
1548 | break; |
1549 | } |
1550 | } |
1551 | |
1552 | /* |
1553 | * If none was installed or the installed one if the last |
1554 | * one on the list, select the first one from the list |
1555 | */ |
1556 | if ((installed_dr == NULL) || (selected_dr == NULL)) { |
1557 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
1558 | dr = TAILQ_NEXT(dr, dr_entry)) { |
1559 | if (dr->ifp == ifp) { |
1560 | selected_dr = dr; |
1561 | break; |
1562 | } |
1563 | } |
1564 | } |
1565 | |
1566 | if ((selected_dr == NULL) && (installed_dr == NULL)) { |
1567 | nd6log2((LOG_INFO, |
1568 | "%s:%d: Between release and reaquire of global " |
1569 | "nd6_mutex lock, the list seems to have changed " |
1570 | "and it does not have any default routers for " |
1571 | "interface %s.\n" , |
1572 | __func__, __LINE__, if_name(ifp))); |
1573 | goto out; |
1574 | } |
1575 | |
1576 | if (selected_dr != installed_dr) |
1577 | NDDR_ADDREF(selected_dr); |
1578 | } else if (installed_dr != NULL) { |
1579 | if (installed_dr != selected_dr) { |
1580 | /* |
1581 | * This means that selected default router is reachable |
1582 | * while installed one may or may not be. |
1583 | * Static router should always be considered as reachable |
1584 | * for router selection process. |
1585 | */ |
1586 | if ((installed_dr->stateflags & NDDRF_STATIC) && |
1587 | rtpref(installed_dr) >= rtpref(selected_dr)) { |
1588 | NDDR_REMREF(selected_dr); |
1589 | selected_dr = installed_dr; |
1590 | } else if (is_installed_reachable) { |
1591 | if (rtpref(selected_dr) <= rtpref(installed_dr)) { |
1592 | NDDR_REMREF(selected_dr); |
1593 | selected_dr = installed_dr; |
1594 | } |
1595 | } |
1596 | } else { |
1597 | NDDR_REMREF(selected_dr); |
1598 | } |
1599 | } |
1600 | |
1601 | install_route: |
1602 | /* |
1603 | * If the selected router is different than the installed one, |
1604 | * remove the installed router and install the selected one. |
1605 | * Note that the selected router is never NULL here. |
1606 | * Else check if the route entry scope has to be changed. |
1607 | */ |
1608 | lck_mtx_unlock(nd6_mutex); |
1609 | if (installed_dr != selected_dr) { |
1610 | nd6log((LOG_INFO, |
1611 | "%s:%d: Found a better router for interface " |
1612 | "%s. Installing new default route.\n" , |
1613 | __func__, __LINE__, if_name(ifp))); |
1614 | if (installed_dr != NULL) { |
1615 | defrouter_delreq(installed_dr); |
1616 | } |
1617 | /* |
1618 | * Install scoped route if the interface is not |
1619 | * the default nd6 interface. |
1620 | */ |
1621 | defrouter_addreq(selected_dr, |
1622 | (selected_dr->ifp != nd6_defifp)); |
1623 | } else if (((installed_dr->stateflags & NDDRF_IFSCOPE) && |
1624 | (installed_dr->ifp == nd6_defifp)) || |
1625 | (!(installed_dr->stateflags & NDDRF_IFSCOPE) && |
1626 | (installed_dr->ifp != nd6_defifp))) { |
1627 | nd6log((LOG_INFO, |
1628 | "%s:%d: Need to reinstall default route for interface " |
1629 | "%s as its scope has changed.\n" , |
1630 | __func__, __LINE__, if_name(ifp))); |
1631 | defrouter_delreq(installed_dr); |
1632 | defrouter_addreq(installed_dr, |
1633 | (installed_dr->ifp != nd6_defifp)); |
1634 | } else { |
1635 | nd6log2((LOG_INFO, |
1636 | "%s:%d: No need to change the default " |
1637 | "route for interface %s.\n" , |
1638 | __func__, __LINE__, if_name(ifp))); |
1639 | } |
1640 | lck_mtx_lock(nd6_mutex); |
1641 | out: |
1642 | if (selected_dr && (selected_dr != installed_dr)) |
1643 | NDDR_REMREF(selected_dr); |
1644 | if (installed_dr) |
1645 | NDDR_REMREF(installed_dr); |
1646 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1647 | VERIFY(nd_defrouter_busy); |
1648 | nd_defrouter_busy = FALSE; |
1649 | if (nd_defrouter_waiters > 0) { |
1650 | nd_defrouter_waiters = 0; |
1651 | wakeup(nd_defrouter_waitchan); |
1652 | } |
1653 | } |
1654 | |
1655 | static struct nd_defrouter * |
1656 | defrtrlist_update_common(struct nd_defrouter *new, boolean_t scoped) |
1657 | { |
1658 | struct nd_defrouter *dr, *n; |
1659 | struct ifnet *ifp = new->ifp; |
1660 | struct nd_ifinfo *ndi = NULL; |
1661 | struct timeval caltime; |
1662 | |
1663 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1664 | |
1665 | if ((dr = defrouter_lookup(&new->rtaddr, ifp)) != NULL) { |
1666 | /* entry exists */ |
1667 | if (new->rtlifetime == 0) { |
1668 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); |
1669 | defrtrlist_del(dr); |
1670 | NDDR_REMREF(dr); /* remove list reference */ |
1671 | NDDR_REMREF(dr); |
1672 | dr = NULL; |
1673 | } else { |
1674 | int oldpref = rtpref(dr); |
1675 | struct nd_defrouter *p = NULL; |
1676 | /* override */ |
1677 | dr->flags = new->flags; /* xxx flag check */ |
1678 | dr->rtlifetime = new->rtlifetime; |
1679 | dr->expire = new->expire; |
1680 | |
1681 | /* |
1682 | * If the preference does not change, there's no need |
1683 | * to sort the entries. If Scoped Routing is enabled, |
1684 | * put the primary/non-scoped router at the top of the |
1685 | * list of routers in the same preference band, unless |
1686 | * it's already at that position. |
1687 | */ |
1688 | /* same preference and scoped; just return */ |
1689 | if (rtpref(new) == oldpref && scoped) |
1690 | return (dr); |
1691 | |
1692 | n = TAILQ_FIRST(&nd_defrouter); |
1693 | while (n != NULL) { |
1694 | /* preference changed; sort it */ |
1695 | if (rtpref(new) != oldpref) |
1696 | break; |
1697 | |
1698 | /* not at the top of band; sort it */ |
1699 | if (n != dr && rtpref(n) == oldpref && |
1700 | (!p || rtpref(p) > rtpref(n))) |
1701 | break; |
1702 | |
1703 | p = n; |
1704 | n = TAILQ_NEXT(n, dr_entry); |
1705 | } |
1706 | |
1707 | /* nothing has changed, just return */ |
1708 | if (n == NULL && (scoped || |
1709 | !(dr->stateflags & NDDRF_IFSCOPE))) |
1710 | return (dr); |
1711 | |
1712 | /* |
1713 | * preferred router may be changed, so relocate |
1714 | * this router. |
1715 | * XXX: calling TAILQ_REMOVE directly is a bad manner. |
1716 | * However, since defrtrlist_del() has many side |
1717 | * effects, we intentionally do so here. |
1718 | * defrouter_select() below will handle routing |
1719 | * changes later. |
1720 | */ |
1721 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); |
1722 | new->stateflags = dr->stateflags; |
1723 | |
1724 | n = dr; |
1725 | goto insert; |
1726 | } |
1727 | return (dr); |
1728 | } |
1729 | |
1730 | VERIFY(dr == NULL); |
1731 | |
1732 | /* entry does not exist */ |
1733 | if (new->rtlifetime == 0) { |
1734 | return (NULL); |
1735 | } |
1736 | |
1737 | n = nddr_alloc(M_WAITOK); |
1738 | if (n == NULL) { |
1739 | return (NULL); |
1740 | } |
1741 | |
1742 | ndi = ND_IFINFO(ifp); |
1743 | VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); |
1744 | lck_mtx_lock(&ndi->lock); |
1745 | if (ip6_maxifdefrouters >= 0 && |
1746 | ndi->ndefrouters >= ip6_maxifdefrouters) { |
1747 | lck_mtx_unlock(&ndi->lock); |
1748 | nddr_free(n); |
1749 | return (NULL); |
1750 | } |
1751 | |
1752 | NDDR_ADDREF(n); /* for the nd_defrouter list */ |
1753 | NDDR_ADDREF(n); /* for the caller */ |
1754 | |
1755 | ++nd6_defrouter_genid; |
1756 | ndi->ndefrouters++; |
1757 | VERIFY(ndi->ndefrouters != 0); |
1758 | lck_mtx_unlock(&ndi->lock); |
1759 | |
1760 | nd6log2((LOG_INFO, "%s: allocating defrouter %s\n" , if_name(ifp), |
1761 | ip6_sprintf(&new->rtaddr))); |
1762 | |
1763 | getmicrotime(&caltime); |
1764 | NDDR_LOCK(n); |
1765 | memcpy(&n->rtaddr, &new->rtaddr, sizeof (n->rtaddr)); |
1766 | n->flags = new->flags; |
1767 | n->stateflags = new->stateflags; |
1768 | n->rtlifetime = new->rtlifetime; |
1769 | n->expire = new->expire; |
1770 | n->base_calendartime = caltime.tv_sec; |
1771 | n->base_uptime = net_uptime(); |
1772 | n->ifp = new->ifp; |
1773 | n->err = new->err; |
1774 | NDDR_UNLOCK(n); |
1775 | insert: |
1776 | /* get nd6_service() to be scheduled as soon as it's convenient */ |
1777 | ++nd6_sched_timeout_want; |
1778 | |
1779 | /* |
1780 | * Insert the new router in the Default Router List; |
1781 | * The Default Router List should be in the descending order |
1782 | * of router-preferece. When Scoped Routing is disabled, routers |
1783 | * with the same preference are sorted in the arriving time order; |
1784 | * otherwise, the first entry in the list of routers having the same |
1785 | * preference is the primary default router, when the interface used |
1786 | * by the entry is the default interface. |
1787 | */ |
1788 | |
1789 | /* insert at the end of the group */ |
1790 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
1791 | dr = TAILQ_NEXT(dr, dr_entry)) { |
1792 | if (rtpref(n) > rtpref(dr) || |
1793 | (!scoped && rtpref(n) == rtpref(dr))) |
1794 | break; |
1795 | } |
1796 | if (dr) |
1797 | TAILQ_INSERT_BEFORE(dr, n, dr_entry); |
1798 | else |
1799 | TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); |
1800 | |
1801 | defrouter_select(ifp); |
1802 | |
1803 | return (n); |
1804 | } |
1805 | |
1806 | static struct nd_defrouter * |
1807 | defrtrlist_update(struct nd_defrouter *new) |
1808 | { |
1809 | struct nd_defrouter *dr; |
1810 | |
1811 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1812 | dr = defrtrlist_update_common(new, |
1813 | (nd6_defifp != NULL && new->ifp != nd6_defifp)); |
1814 | |
1815 | return (dr); |
1816 | } |
1817 | |
1818 | static struct nd_pfxrouter * |
1819 | pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) |
1820 | { |
1821 | struct nd_pfxrouter *search; |
1822 | |
1823 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1824 | NDPR_LOCK_ASSERT_HELD(pr); |
1825 | |
1826 | for (search = pr->ndpr_advrtrs.lh_first; search; |
1827 | search = search->pfr_next) { |
1828 | if (search->router == dr) |
1829 | break; |
1830 | } |
1831 | |
1832 | return (search); |
1833 | } |
1834 | |
1835 | static void |
1836 | pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr) |
1837 | { |
1838 | struct nd_pfxrouter *new; |
1839 | |
1840 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1841 | NDPR_LOCK_ASSERT_NOTHELD(pr); |
1842 | |
1843 | new = zalloc(ndprtr_zone); |
1844 | if (new == NULL) |
1845 | return; |
1846 | bzero(new, sizeof (*new)); |
1847 | new->router = dr; |
1848 | |
1849 | NDPR_LOCK(pr); |
1850 | LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); |
1851 | pr->ndpr_genid++; |
1852 | NDPR_UNLOCK(pr); |
1853 | |
1854 | pfxlist_onlink_check(); |
1855 | } |
1856 | |
1857 | static void |
1858 | pfxrtr_del(struct nd_pfxrouter *pfr, struct nd_prefix *pr) |
1859 | { |
1860 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1861 | NDPR_LOCK_ASSERT_HELD(pr); |
1862 | pr->ndpr_genid++; |
1863 | LIST_REMOVE(pfr, pfr_entry); |
1864 | zfree(ndprtr_zone, pfr); |
1865 | } |
1866 | |
1867 | /* |
1868 | * The routine has been modified to atomically refresh expiry |
1869 | * time for nd6 prefix as the part of lookup. |
1870 | * There's a corner case where a system going |
1871 | * in sleep gets rid of manual addresses configured in the system |
1872 | * and then schedules the prefix for deletion. |
1873 | * However before the prefix gets deleted, if system comes out |
1874 | * from sleep and configures same address before prefix deletion |
1875 | * , the later prefix deletion will remove the prefix route and |
1876 | * the system will not be able to communicate with other IPv6 |
1877 | * neighbor nodes in the same subnet. |
1878 | */ |
1879 | struct nd_prefix * |
1880 | nd6_prefix_lookup(struct nd_prefix *pr, int nd6_prefix_expiry) |
1881 | { |
1882 | struct nd_prefix *search; |
1883 | |
1884 | lck_mtx_lock(nd6_mutex); |
1885 | for (search = nd_prefix.lh_first; search; search = search->ndpr_next) { |
1886 | NDPR_LOCK(search); |
1887 | if (pr->ndpr_ifp == search->ndpr_ifp && |
1888 | pr->ndpr_plen == search->ndpr_plen && |
1889 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, |
1890 | &search->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { |
1891 | if (nd6_prefix_expiry != ND6_PREFIX_EXPIRY_UNSPEC) { |
1892 | search->ndpr_expire = nd6_prefix_expiry; |
1893 | } |
1894 | NDPR_ADDREF_LOCKED(search); |
1895 | NDPR_UNLOCK(search); |
1896 | break; |
1897 | } |
1898 | NDPR_UNLOCK(search); |
1899 | } |
1900 | lck_mtx_unlock(nd6_mutex); |
1901 | |
1902 | return (search); |
1903 | } |
1904 | |
1905 | int |
1906 | nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr, |
1907 | struct nd_prefix **newp, boolean_t force_scoped) |
1908 | { |
1909 | struct nd_prefix *new = NULL; |
1910 | struct ifnet *ifp = pr->ndpr_ifp; |
1911 | struct nd_ifinfo *ndi = NULL; |
1912 | int i, error; |
1913 | |
1914 | if (ip6_maxifprefixes >= 0) { |
1915 | ndi = ND_IFINFO(ifp); |
1916 | VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); |
1917 | lck_mtx_lock(&ndi->lock); |
1918 | if (ndi->nprefixes >= ip6_maxifprefixes) { |
1919 | lck_mtx_unlock(&ndi->lock); |
1920 | return (ENOMEM); |
1921 | } |
1922 | lck_mtx_unlock(&ndi->lock); |
1923 | } |
1924 | |
1925 | new = ndpr_alloc(M_WAITOK); |
1926 | if (new == NULL) |
1927 | return (ENOMEM); |
1928 | |
1929 | NDPR_LOCK(new); |
1930 | NDPR_LOCK(pr); |
1931 | new->ndpr_ifp = pr->ndpr_ifp; |
1932 | new->ndpr_prefix = pr->ndpr_prefix; |
1933 | new->ndpr_plen = pr->ndpr_plen; |
1934 | new->ndpr_vltime = pr->ndpr_vltime; |
1935 | new->ndpr_pltime = pr->ndpr_pltime; |
1936 | new->ndpr_flags = pr->ndpr_flags; |
1937 | if (pr->ndpr_stateflags & NDPRF_STATIC) |
1938 | new->ndpr_stateflags |= NDPRF_STATIC; |
1939 | NDPR_UNLOCK(pr); |
1940 | if ((error = in6_init_prefix_ltimes(new)) != 0) { |
1941 | NDPR_UNLOCK(new); |
1942 | ndpr_free(new); |
1943 | return (error); |
1944 | } |
1945 | new->ndpr_lastupdate = net_uptime(); |
1946 | if (newp != NULL) { |
1947 | *newp = new; |
1948 | NDPR_ADDREF_LOCKED(new); /* for caller */ |
1949 | } |
1950 | /* initialization */ |
1951 | LIST_INIT(&new->ndpr_advrtrs); |
1952 | in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); |
1953 | /* make prefix in the canonical form */ |
1954 | for (i = 0; i < 4; i++) |
1955 | new->ndpr_prefix.sin6_addr.s6_addr32[i] &= |
1956 | new->ndpr_mask.s6_addr32[i]; |
1957 | |
1958 | NDPR_UNLOCK(new); |
1959 | |
1960 | /* get nd6_service() to be scheduled as soon as it's convenient */ |
1961 | ++nd6_sched_timeout_want; |
1962 | |
1963 | lck_mtx_lock(nd6_mutex); |
1964 | /* link ndpr_entry to nd_prefix list */ |
1965 | LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); |
1966 | new->ndpr_debug |= IFD_ATTACHED; |
1967 | NDPR_ADDREF(new); /* for nd_prefix list */ |
1968 | |
1969 | lck_mtx_lock(&ndi->lock); |
1970 | ndi->nprefixes++; |
1971 | VERIFY(ndi->nprefixes != 0); |
1972 | lck_mtx_unlock(&ndi->lock); |
1973 | |
1974 | /* ND_OPT_PI_FLAG_ONLINK processing */ |
1975 | if (new->ndpr_raf_onlink) { |
1976 | int e; |
1977 | |
1978 | if ((e = nd6_prefix_onlink_common(new, force_scoped, |
1979 | new->ndpr_ifp->if_index)) != 0) { |
1980 | nd6log((LOG_ERR, "nd6_prelist_add: failed to make " |
1981 | "the prefix %s/%d on-link %s on %s (errno=%d)\n" , |
1982 | ip6_sprintf(&new->ndpr_prefix.sin6_addr), |
1983 | new->ndpr_plen, force_scoped ? "scoped" : |
1984 | "non-scoped" , if_name(ifp), e)); |
1985 | /* proceed anyway. XXX: is it correct? */ |
1986 | } |
1987 | } |
1988 | |
1989 | if (dr) { |
1990 | pfxrtr_add(new, dr); |
1991 | } |
1992 | |
1993 | lck_mtx_unlock(nd6_mutex); |
1994 | |
1995 | return (0); |
1996 | } |
1997 | |
1998 | /* |
1999 | * Caller must have held an extra reference on nd_prefix. |
2000 | */ |
2001 | void |
2002 | prelist_remove(struct nd_prefix *pr) |
2003 | { |
2004 | struct nd_pfxrouter *pfr, *next; |
2005 | struct ifnet *ifp = pr->ndpr_ifp; |
2006 | int e; |
2007 | struct nd_ifinfo *ndi = NULL; |
2008 | |
2009 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
2010 | NDPR_LOCK_ASSERT_HELD(pr); |
2011 | |
2012 | if (pr->ndpr_stateflags & NDPRF_DEFUNCT) |
2013 | return; |
2014 | |
2015 | /* |
2016 | * If there are no more addresses, defunct the prefix. This is needed |
2017 | * because we don't want multiple threads calling prelist_remove() for |
2018 | * the same prefix and this might happen because we unlock nd6_mutex |
2019 | * down below. |
2020 | */ |
2021 | if (pr->ndpr_addrcnt == 0) |
2022 | pr->ndpr_stateflags |= NDPRF_DEFUNCT; |
2023 | |
2024 | /* make sure to invalidate the prefix until it is really freed. */ |
2025 | pr->ndpr_vltime = 0; |
2026 | pr->ndpr_pltime = 0; |
2027 | |
2028 | /* |
2029 | * Though these flags are now meaningless, we'd rather keep the value |
2030 | * of pr->ndpr_raf_onlink and pr->ndpr_raf_auto not to confuse users |
2031 | * when executing "ndp -p". |
2032 | */ |
2033 | if (pr->ndpr_stateflags & NDPRF_ONLINK) { |
2034 | NDPR_ADDREF_LOCKED(pr); |
2035 | NDPR_UNLOCK(pr); |
2036 | lck_mtx_unlock(nd6_mutex); |
2037 | if ((e = nd6_prefix_offlink(pr)) != 0) { |
2038 | nd6log((LOG_ERR, "prelist_remove: failed to make " |
2039 | "%s/%d offlink on %s, errno=%d\n" , |
2040 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
2041 | pr->ndpr_plen, if_name(ifp), e)); |
2042 | /* what should we do? */ |
2043 | } |
2044 | lck_mtx_lock(nd6_mutex); |
2045 | NDPR_LOCK(pr); |
2046 | if (NDPR_REMREF_LOCKED(pr) == NULL) |
2047 | return; |
2048 | } |
2049 | |
2050 | if (pr->ndpr_addrcnt > 0) { |
2051 | /* |
2052 | * The state might have changed if we called |
2053 | * nd6_prefix_offlink(). |
2054 | */ |
2055 | pr->ndpr_stateflags &= ~NDPRF_DEFUNCT; |
2056 | return; /* notice here? */ |
2057 | } |
2058 | |
2059 | /* unlink ndpr_entry from nd_prefix list */ |
2060 | LIST_REMOVE(pr, ndpr_entry); |
2061 | pr->ndpr_debug &= ~IFD_ATTACHED; |
2062 | |
2063 | /* free list of routers that adversed the prefix */ |
2064 | for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { |
2065 | next = pfr->pfr_next; |
2066 | pfxrtr_del(pfr, pr); |
2067 | } |
2068 | |
2069 | ndi = ND_IFINFO(ifp); |
2070 | VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); |
2071 | lck_mtx_lock(&ndi->lock); |
2072 | VERIFY(ndi->nprefixes > 0); |
2073 | ndi->nprefixes--; |
2074 | lck_mtx_unlock(&ndi->lock); |
2075 | |
2076 | /* This must not be the last reference to the nd_prefix */ |
2077 | if (NDPR_REMREF_LOCKED(pr) == NULL) { |
2078 | panic("%s: unexpected (missing) refcnt ndpr=%p" , __func__, pr); |
2079 | /* NOTREACHED */ |
2080 | } |
2081 | |
2082 | /* |
2083 | * Don't call pfxlist_onlink_check() here because we are |
2084 | * holding the NDPR lock and this could cause a deadlock when |
2085 | * there are multiple threads executing pfxlist_onlink_check(). |
2086 | */ |
2087 | } |
2088 | |
2089 | int |
2090 | prelist_update( |
2091 | struct nd_prefix *new, |
2092 | struct nd_defrouter *dr, /* may be NULL */ |
2093 | struct mbuf *m, |
2094 | int mcast) |
2095 | { |
2096 | struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; |
2097 | struct ifaddr *ifa; |
2098 | struct ifnet *ifp = new->ndpr_ifp; |
2099 | struct nd_prefix *pr; |
2100 | int error = 0; |
2101 | int newprefix = 0; |
2102 | int auth; |
2103 | struct in6_addrlifetime lt6_tmp; |
2104 | uint64_t timenow = net_uptime(); |
2105 | |
2106 | /* no need to lock "new" here, as it is local to the caller */ |
2107 | NDPR_LOCK_ASSERT_NOTHELD(new); |
2108 | |
2109 | auth = 0; |
2110 | if (m) { |
2111 | /* |
2112 | * Authenticity for NA consists authentication for |
2113 | * both IP header and IP datagrams, doesn't it ? |
2114 | */ |
2115 | #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) |
2116 | auth = (m->m_flags & M_AUTHIPHDR) && (m->m_flags & M_AUTHIPDGM); |
2117 | #endif |
2118 | } |
2119 | |
2120 | if ((pr = nd6_prefix_lookup(new, ND6_PREFIX_EXPIRY_UNSPEC)) != NULL) { |
2121 | /* |
2122 | * nd6_prefix_lookup() ensures that pr and new have the same |
2123 | * prefix on a same interface. |
2124 | */ |
2125 | |
2126 | /* |
2127 | * Update prefix information. Note that the on-link (L) bit |
2128 | * and the autonomous (A) bit should NOT be changed from 1 |
2129 | * to 0. |
2130 | */ |
2131 | lck_mtx_lock(nd6_mutex); |
2132 | NDPR_LOCK(pr); |
2133 | if (new->ndpr_raf_onlink == 1) |
2134 | pr->ndpr_raf_onlink = 1; |
2135 | if (new->ndpr_raf_auto == 1) |
2136 | pr->ndpr_raf_auto = 1; |
2137 | if (new->ndpr_raf_onlink) { |
2138 | pr->ndpr_vltime = new->ndpr_vltime; |
2139 | pr->ndpr_pltime = new->ndpr_pltime; |
2140 | (void) in6_init_prefix_ltimes(pr); /* XXX error case? */ |
2141 | pr->ndpr_lastupdate = net_uptime(); |
2142 | } |
2143 | |
2144 | NDPR_ADDREF_LOCKED(pr); |
2145 | if (new->ndpr_raf_onlink && |
2146 | (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { |
2147 | int e; |
2148 | |
2149 | NDPR_UNLOCK(pr); |
2150 | if ((e = nd6_prefix_onlink(pr)) != 0) { |
2151 | nd6log((LOG_ERR, |
2152 | "prelist_update: failed to make " |
2153 | "the prefix %s/%d on-link on %s " |
2154 | "(errno=%d)\n" , |
2155 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
2156 | pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); |
2157 | /* proceed anyway. XXX: is it correct? */ |
2158 | } |
2159 | NDPR_LOCK(pr); |
2160 | } |
2161 | |
2162 | if (dr && pfxrtr_lookup(pr, dr) == NULL) { |
2163 | NDPR_UNLOCK(pr); |
2164 | pfxrtr_add(pr, dr); |
2165 | } else { |
2166 | NDPR_UNLOCK(pr); |
2167 | } |
2168 | NDPR_REMREF(pr); |
2169 | lck_mtx_unlock(nd6_mutex); |
2170 | } else { |
2171 | newprefix = 1; |
2172 | |
2173 | if (new->ndpr_vltime == 0) |
2174 | goto end; |
2175 | if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) |
2176 | goto end; |
2177 | |
2178 | bzero(&new->ndpr_addr, sizeof (struct in6_addr)); |
2179 | |
2180 | error = nd6_prelist_add(new, dr, &pr, FALSE); |
2181 | if (error != 0 || pr == NULL) { |
2182 | nd6log((LOG_NOTICE, "prelist_update: " |
2183 | "nd6_prelist_add failed for %s/%d on %s " |
2184 | "errno=%d, returnpr=0x%llx\n" , |
2185 | ip6_sprintf(&new->ndpr_prefix.sin6_addr), |
2186 | new->ndpr_plen, if_name(new->ndpr_ifp), |
2187 | error, (uint64_t)VM_KERNEL_ADDRPERM(pr))); |
2188 | goto end; /* we should just give up in this case. */ |
2189 | } |
2190 | } |
2191 | |
2192 | /* |
2193 | * Address autoconfiguration based on Section 5.5.3 of RFC 4862. |
2194 | * Note that pr must be non NULL at this point. |
2195 | */ |
2196 | |
2197 | /* 5.5.3 (a). Ignore the prefix without the A bit set. */ |
2198 | if (!new->ndpr_raf_auto) |
2199 | goto end; |
2200 | |
2201 | /* |
2202 | * 5.5.3 (b). the link-local prefix should have been ignored in |
2203 | * nd6_ra_input. |
2204 | */ |
2205 | |
2206 | /* 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. */ |
2207 | if (new->ndpr_pltime > new->ndpr_vltime) { |
2208 | error = EINVAL; /* XXX: won't be used */ |
2209 | goto end; |
2210 | } |
2211 | |
2212 | /* |
2213 | * 5.5.3 (d). If the prefix advertised is not equal to the prefix of |
2214 | * an address configured by stateless autoconfiguration already in the |
2215 | * list of addresses associated with the interface, and the Valid |
2216 | * Lifetime is not 0, form an address. We first check if we have |
2217 | * a matching prefix. |
2218 | */ |
2219 | ifnet_lock_shared(ifp); |
2220 | TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { |
2221 | struct in6_ifaddr *ifa6; |
2222 | u_int32_t remaininglifetime; |
2223 | |
2224 | IFA_LOCK(ifa); |
2225 | if (ifa->ifa_addr->sa_family != AF_INET6) { |
2226 | IFA_UNLOCK(ifa); |
2227 | continue; |
2228 | } |
2229 | ifa6 = (struct in6_ifaddr *)ifa; |
2230 | |
2231 | /* |
2232 | * We only consider autoconfigured addresses as per RFC 4862. |
2233 | */ |
2234 | if (!(ifa6->ia6_flags & IN6_IFF_AUTOCONF)) { |
2235 | IFA_UNLOCK(ifa); |
2236 | continue; |
2237 | } |
2238 | /* |
2239 | * Spec is not clear here, but I believe we should concentrate |
2240 | * on unicast (i.e. not anycast) addresses. |
2241 | * XXX: other ia6_flags? detached or duplicated? |
2242 | */ |
2243 | if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) { |
2244 | IFA_UNLOCK(ifa); |
2245 | continue; |
2246 | } |
2247 | /* |
2248 | * Ignore the address if it is not associated with a prefix |
2249 | * or is associated with a prefix that is different from this |
2250 | * one. (pr is never NULL here) |
2251 | */ |
2252 | if (ifa6->ia6_ndpr != pr) { |
2253 | IFA_UNLOCK(ifa); |
2254 | continue; |
2255 | } |
2256 | |
2257 | if (ia6_match == NULL) { /* remember the first one */ |
2258 | ia6_match = ifa6; |
2259 | IFA_ADDREF_LOCKED(ifa); /* for ia6_match */ |
2260 | } |
2261 | |
2262 | /* |
2263 | * An already autoconfigured address matched. Now that we |
2264 | * are sure there is at least one matched address, we can |
2265 | * proceed to 5.5.3. (e): update the lifetimes according to the |
2266 | * "two hours" rule and the privacy extension. |
2267 | */ |
2268 | #define TWOHOUR (120*60) |
2269 | |
2270 | /* retrieve time as uptime (last arg is 0) */ |
2271 | in6ifa_getlifetime(ifa6, <6_tmp, 0); |
2272 | |
2273 | if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME) |
2274 | remaininglifetime = ND6_INFINITE_LIFETIME; |
2275 | else if (timenow - ifa6->ia6_updatetime > lt6_tmp.ia6t_vltime) { |
2276 | /* |
2277 | * The case of "invalid" address. We should usually |
2278 | * not see this case. |
2279 | */ |
2280 | remaininglifetime = 0; |
2281 | } else { |
2282 | remaininglifetime = lt6_tmp.ia6t_vltime - |
2283 | (timenow - ifa6->ia6_updatetime); |
2284 | } |
2285 | /* when not updating, keep the current stored lifetime. */ |
2286 | lt6_tmp.ia6t_vltime = remaininglifetime; |
2287 | |
2288 | if (TWOHOUR < new->ndpr_vltime || |
2289 | remaininglifetime < new->ndpr_vltime) { |
2290 | lt6_tmp.ia6t_vltime = new->ndpr_vltime; |
2291 | } else if (remaininglifetime <= TWOHOUR) { |
2292 | if (auth) { |
2293 | lt6_tmp.ia6t_vltime = new->ndpr_vltime; |
2294 | } |
2295 | } else { |
2296 | /* |
2297 | * new->ndpr_vltime <= TWOHOUR && |
2298 | * TWOHOUR < remaininglifetime |
2299 | */ |
2300 | lt6_tmp.ia6t_vltime = TWOHOUR; |
2301 | } |
2302 | |
2303 | /* The 2 hour rule is not imposed for preferred lifetime. */ |
2304 | lt6_tmp.ia6t_pltime = new->ndpr_pltime; |
2305 | |
2306 | /* Special handling for lifetimes of temporary addresses. */ |
2307 | if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { |
2308 | u_int32_t maxvltime, maxpltime; |
2309 | |
2310 | /* Constrain lifetimes to system limits. */ |
2311 | if (lt6_tmp.ia6t_vltime > ip6_temp_valid_lifetime) |
2312 | lt6_tmp.ia6t_vltime = ip6_temp_valid_lifetime; |
2313 | if (lt6_tmp.ia6t_pltime > ip6_temp_preferred_lifetime) |
2314 | lt6_tmp.ia6t_pltime = |
2315 | ip6_temp_preferred_lifetime - |
2316 | ip6_desync_factor; |
2317 | |
2318 | /* |
2319 | * According to RFC 4941, section 3.3 (1), we only |
2320 | * update the lifetimes when they are in the maximum |
2321 | * intervals. |
2322 | */ |
2323 | if (ip6_temp_valid_lifetime > |
2324 | (u_int32_t)((timenow - ifa6->ia6_createtime) + |
2325 | ip6_desync_factor)) { |
2326 | maxvltime = ip6_temp_valid_lifetime - |
2327 | (timenow - ifa6->ia6_createtime) - |
2328 | ip6_desync_factor; |
2329 | } else |
2330 | maxvltime = 0; |
2331 | if (ip6_temp_preferred_lifetime > |
2332 | (u_int32_t)((timenow - ifa6->ia6_createtime) + |
2333 | ip6_desync_factor)) { |
2334 | maxpltime = ip6_temp_preferred_lifetime - |
2335 | (timenow - ifa6->ia6_createtime) - |
2336 | ip6_desync_factor; |
2337 | } else |
2338 | maxpltime = 0; |
2339 | |
2340 | if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME || |
2341 | lt6_tmp.ia6t_vltime > maxvltime) |
2342 | lt6_tmp.ia6t_vltime = maxvltime; |
2343 | |
2344 | if (lt6_tmp.ia6t_pltime == ND6_INFINITE_LIFETIME || |
2345 | lt6_tmp.ia6t_pltime > maxpltime) |
2346 | lt6_tmp.ia6t_pltime = maxpltime; |
2347 | } |
2348 | |
2349 | in6_init_address_ltimes(pr, <6_tmp); |
2350 | |
2351 | in6ifa_setlifetime(ifa6, <6_tmp); |
2352 | ifa6->ia6_updatetime = timenow; |
2353 | IFA_UNLOCK(ifa); |
2354 | } |
2355 | ifnet_lock_done(ifp); |
2356 | if (ia6_match == NULL && new->ndpr_vltime) { |
2357 | /* |
2358 | * 5.5.3 (d) (continued) |
2359 | * No address matched and the valid lifetime is non-zero. |
2360 | * Create a new address. |
2361 | */ |
2362 | if ((ia6 = in6_pfx_newpersistaddr(new, mcast, &error, FALSE)) |
2363 | != NULL) { |
2364 | /* |
2365 | * note that we should use pr (not new) for reference. |
2366 | */ |
2367 | IFA_LOCK(&ia6->ia_ifa); |
2368 | NDPR_LOCK(pr); |
2369 | ia6->ia6_ndpr = pr; |
2370 | NDPR_ADDREF_LOCKED(pr); /* for addr reference */ |
2371 | pr->ndpr_addrcnt++; |
2372 | VERIFY(pr->ndpr_addrcnt != 0); |
2373 | NDPR_UNLOCK(pr); |
2374 | IFA_UNLOCK(&ia6->ia_ifa); |
2375 | |
2376 | /* |
2377 | * RFC 4941 3.3 (2). |
2378 | * When a new public address is created as described |
2379 | * in RFC 4862, also create a new temporary address. |
2380 | * |
2381 | * RFC 4941 3.5. |
2382 | * When an interface connects to a new link, a new |
2383 | * randomized interface identifier should be generated |
2384 | * immediately together with a new set of temporary |
2385 | * addresses. Thus, we specifiy 1 as the 2nd arg of |
2386 | * in6_tmpifadd(). |
2387 | */ |
2388 | if (ip6_use_tempaddr) { |
2389 | int e; |
2390 | if ((e = in6_tmpifadd(ia6, 1)) != 0) { |
2391 | nd6log((LOG_NOTICE, "prelist_update: " |
2392 | "failed to create a temporary " |
2393 | "address, errno=%d\n" , |
2394 | e)); |
2395 | } |
2396 | } |
2397 | IFA_REMREF(&ia6->ia_ifa); |
2398 | ia6 = NULL; |
2399 | |
2400 | /* |
2401 | * If the interface is marked for CLAT46 configuration |
2402 | * try and configure the reserved IPv6 address for |
2403 | * stateless translation. |
2404 | */ |
2405 | if (IS_INTF_CLAT46(ifp)) { |
2406 | if ((ia6 = in6_pfx_newpersistaddr(new, mcast,&error, TRUE)) != NULL) { |
2407 | IFA_LOCK(&ia6->ia_ifa); |
2408 | NDPR_LOCK(pr); |
2409 | ia6->ia6_ndpr = pr; |
2410 | NDPR_ADDREF_LOCKED(pr); /* for addr reference */ |
2411 | pr->ndpr_addrcnt++; |
2412 | VERIFY(pr->ndpr_addrcnt != 0); |
2413 | pr->ndpr_stateflags |= NDPRF_CLAT46; |
2414 | NDPR_UNLOCK(pr); |
2415 | IFA_UNLOCK(&ia6->ia_ifa); |
2416 | IFA_REMREF(&ia6->ia_ifa); |
2417 | ia6 = NULL; |
2418 | } else if (error != EEXIST) { |
2419 | uuid_t tmp_uuid = {}; |
2420 | /* |
2421 | * Only report the error if it is not |
2422 | * EEXIST. |
2423 | */ |
2424 | ip6stat.ip6s_clat464_v6addr_conffail++; |
2425 | in6_clat46_event_enqueue_nwk_wq_entry( |
2426 | IN6_CLAT46_EVENT_V6_ADDR_CONFFAIL, |
2427 | 0, |
2428 | tmp_uuid); |
2429 | nd6log0((LOG_ERR, "Could not configure CLAT46 address on interface " |
2430 | "%s.\n" , ifp->if_xname)); |
2431 | } |
2432 | /* |
2433 | * Reset the error as we do not want to |
2434 | * treat failure of CLAT46 address configuration |
2435 | * as complete failure in prelist update path. |
2436 | */ |
2437 | error = 0; |
2438 | } |
2439 | |
2440 | /* |
2441 | * A newly added address might affect the status |
2442 | * of other addresses, so we check and update it. |
2443 | * XXX: what if address duplication happens? |
2444 | */ |
2445 | lck_mtx_lock(nd6_mutex); |
2446 | pfxlist_onlink_check(); |
2447 | lck_mtx_unlock(nd6_mutex); |
2448 | } |
2449 | } |
2450 | end: |
2451 | if (pr != NULL) |
2452 | NDPR_REMREF(pr); |
2453 | if (ia6_match != NULL) |
2454 | IFA_REMREF(&ia6_match->ia_ifa); |
2455 | return (error); |
2456 | } |
2457 | |
2458 | /* |
2459 | * Neighbor Discover Default Router structure reference counting routines. |
2460 | */ |
2461 | static struct nd_defrouter * |
2462 | nddr_alloc(int how) |
2463 | { |
2464 | struct nd_defrouter *dr; |
2465 | |
2466 | dr = (how == M_WAITOK) ? zalloc(nddr_zone) : zalloc_noblock(nddr_zone); |
2467 | if (dr != NULL) { |
2468 | bzero(dr, nddr_size); |
2469 | lck_mtx_init(&dr->nddr_lock, ifa_mtx_grp, ifa_mtx_attr); |
2470 | dr->nddr_debug |= IFD_ALLOC; |
2471 | if (nddr_debug != 0) { |
2472 | dr->nddr_debug |= IFD_DEBUG; |
2473 | dr->nddr_trace = nddr_trace; |
2474 | } |
2475 | } |
2476 | return (dr); |
2477 | } |
2478 | |
2479 | static void |
2480 | nddr_free(struct nd_defrouter *dr) |
2481 | { |
2482 | NDDR_LOCK(dr); |
2483 | if (dr->nddr_debug & IFD_ATTACHED) { |
2484 | panic("%s: attached nddr %p is being freed" , __func__, dr); |
2485 | /* NOTREACHED */ |
2486 | } else if (!(dr->nddr_debug & IFD_ALLOC)) { |
2487 | panic("%s: nddr %p cannot be freed" , __func__, dr); |
2488 | /* NOTREACHED */ |
2489 | } |
2490 | dr->nddr_debug &= ~IFD_ALLOC; |
2491 | NDDR_UNLOCK(dr); |
2492 | |
2493 | lck_mtx_destroy(&dr->nddr_lock, ifa_mtx_grp); |
2494 | zfree(nddr_zone, dr); |
2495 | } |
2496 | |
2497 | static void |
2498 | nddr_trace(struct nd_defrouter *dr, int refhold) |
2499 | { |
2500 | struct nd_defrouter_dbg *dr_dbg = (struct nd_defrouter_dbg *)dr; |
2501 | ctrace_t *tr; |
2502 | uint32_t idx; |
2503 | uint16_t *cnt; |
2504 | |
2505 | if (!(dr->nddr_debug & IFD_DEBUG)) { |
2506 | panic("%s: nddr %p has no debug structure" , __func__, dr); |
2507 | /* NOTREACHED */ |
2508 | } |
2509 | if (refhold) { |
2510 | cnt = &dr_dbg->nddr_refhold_cnt; |
2511 | tr = dr_dbg->nddr_refhold; |
2512 | } else { |
2513 | cnt = &dr_dbg->nddr_refrele_cnt; |
2514 | tr = dr_dbg->nddr_refrele; |
2515 | } |
2516 | |
2517 | idx = atomic_add_16_ov(cnt, 1) % NDDR_TRACE_HIST_SIZE; |
2518 | ctrace_record(&tr[idx]); |
2519 | } |
2520 | |
2521 | void |
2522 | nddr_addref(struct nd_defrouter *nddr, int locked) |
2523 | { |
2524 | |
2525 | if (!locked) |
2526 | NDDR_LOCK_SPIN(nddr); |
2527 | else |
2528 | NDDR_LOCK_ASSERT_HELD(nddr); |
2529 | |
2530 | if (++nddr->nddr_refcount == 0) { |
2531 | panic("%s: nddr %p wraparound refcnt\n" , __func__, nddr); |
2532 | /* NOTREACHED */ |
2533 | } else if (nddr->nddr_trace != NULL) { |
2534 | (*nddr->nddr_trace)(nddr, TRUE); |
2535 | } |
2536 | |
2537 | if (!locked) |
2538 | NDDR_UNLOCK(nddr); |
2539 | } |
2540 | |
2541 | struct nd_defrouter * |
2542 | nddr_remref(struct nd_defrouter *nddr, int locked) |
2543 | { |
2544 | |
2545 | if (!locked) |
2546 | NDDR_LOCK_SPIN(nddr); |
2547 | else |
2548 | NDDR_LOCK_ASSERT_HELD(nddr); |
2549 | |
2550 | if (nddr->nddr_refcount == 0) { |
2551 | panic("%s: nddr %p negative refcnt\n" , __func__, nddr); |
2552 | /* NOTREACHED */ |
2553 | } else if (nddr->nddr_trace != NULL) { |
2554 | (*nddr->nddr_trace)(nddr, FALSE); |
2555 | } |
2556 | |
2557 | if (--nddr->nddr_refcount == 0) { |
2558 | NDDR_UNLOCK(nddr); |
2559 | nddr_free(nddr); |
2560 | nddr = NULL; |
2561 | } |
2562 | |
2563 | if (!locked && nddr != NULL) |
2564 | NDDR_UNLOCK(nddr); |
2565 | |
2566 | return (nddr); |
2567 | } |
2568 | |
2569 | uint64_t |
2570 | nddr_getexpire(struct nd_defrouter *dr) |
2571 | { |
2572 | struct timeval caltime; |
2573 | uint64_t expiry; |
2574 | |
2575 | if (dr->expire != 0) { |
2576 | /* account for system time change */ |
2577 | getmicrotime(&caltime); |
2578 | |
2579 | dr->base_calendartime += |
2580 | NET_CALCULATE_CLOCKSKEW(caltime, |
2581 | dr->base_calendartime, net_uptime(), dr->base_uptime); |
2582 | |
2583 | expiry = dr->base_calendartime + |
2584 | dr->expire - dr->base_uptime; |
2585 | } else { |
2586 | expiry = 0; |
2587 | } |
2588 | return (expiry); |
2589 | } |
2590 | |
2591 | /* |
2592 | * Neighbor Discover Prefix structure reference counting routines. |
2593 | */ |
2594 | static struct nd_prefix * |
2595 | ndpr_alloc(int how) |
2596 | { |
2597 | struct nd_prefix *pr; |
2598 | |
2599 | pr = (how == M_WAITOK) ? zalloc(ndpr_zone) : zalloc_noblock(ndpr_zone); |
2600 | if (pr != NULL) { |
2601 | bzero(pr, ndpr_size); |
2602 | lck_mtx_init(&pr->ndpr_lock, ifa_mtx_grp, ifa_mtx_attr); |
2603 | RB_INIT(&pr->ndpr_prproxy_sols); |
2604 | pr->ndpr_debug |= IFD_ALLOC; |
2605 | if (ndpr_debug != 0) { |
2606 | pr->ndpr_debug |= IFD_DEBUG; |
2607 | pr->ndpr_trace = ndpr_trace; |
2608 | } |
2609 | } |
2610 | return (pr); |
2611 | } |
2612 | |
2613 | static void |
2614 | ndpr_free(struct nd_prefix *pr) |
2615 | { |
2616 | NDPR_LOCK(pr); |
2617 | if (pr->ndpr_debug & IFD_ATTACHED) { |
2618 | panic("%s: attached ndpr %p is being freed" , __func__, pr); |
2619 | /* NOTREACHED */ |
2620 | } else if (!(pr->ndpr_debug & IFD_ALLOC)) { |
2621 | panic("%s: ndpr %p cannot be freed" , __func__, pr); |
2622 | /* NOTREACHED */ |
2623 | } else if (pr->ndpr_rt != NULL) { |
2624 | panic("%s: ndpr %p route %p not freed" , __func__, pr, |
2625 | pr->ndpr_rt); |
2626 | /* NOTREACHED */ |
2627 | } else if (pr->ndpr_prproxy_sols_cnt != 0) { |
2628 | panic("%s: ndpr %p non-zero solicitors count (%d)" , |
2629 | __func__, pr, pr->ndpr_prproxy_sols_cnt); |
2630 | /* NOTREACHED */ |
2631 | } else if (!RB_EMPTY(&pr->ndpr_prproxy_sols)) { |
2632 | panic("%s: ndpr %p non-empty solicitors tree" , __func__, pr); |
2633 | /* NOTREACHED */ |
2634 | } |
2635 | pr->ndpr_debug &= ~IFD_ALLOC; |
2636 | NDPR_UNLOCK(pr); |
2637 | |
2638 | lck_mtx_destroy(&pr->ndpr_lock, ifa_mtx_grp); |
2639 | zfree(ndpr_zone, pr); |
2640 | } |
2641 | |
2642 | static void |
2643 | ndpr_trace(struct nd_prefix *pr, int refhold) |
2644 | { |
2645 | struct nd_prefix_dbg *pr_dbg = (struct nd_prefix_dbg *)pr; |
2646 | ctrace_t *tr; |
2647 | u_int32_t idx; |
2648 | u_int16_t *cnt; |
2649 | |
2650 | if (!(pr->ndpr_debug & IFD_DEBUG)) { |
2651 | panic("%s: ndpr %p has no debug structure" , __func__, pr); |
2652 | /* NOTREACHED */ |
2653 | } |
2654 | if (refhold) { |
2655 | cnt = &pr_dbg->ndpr_refhold_cnt; |
2656 | tr = pr_dbg->ndpr_refhold; |
2657 | } else { |
2658 | cnt = &pr_dbg->ndpr_refrele_cnt; |
2659 | tr = pr_dbg->ndpr_refrele; |
2660 | } |
2661 | |
2662 | idx = atomic_add_16_ov(cnt, 1) % NDPR_TRACE_HIST_SIZE; |
2663 | ctrace_record(&tr[idx]); |
2664 | } |
2665 | |
2666 | void |
2667 | ndpr_addref(struct nd_prefix *ndpr, int locked) |
2668 | { |
2669 | if (!locked) |
2670 | NDPR_LOCK_SPIN(ndpr); |
2671 | else |
2672 | NDPR_LOCK_ASSERT_HELD(ndpr); |
2673 | |
2674 | if (++ndpr->ndpr_refcount == 0) { |
2675 | panic("%s: ndpr %p wraparound refcnt\n" , __func__, ndpr); |
2676 | /* NOTREACHED */ |
2677 | } else if (ndpr->ndpr_trace != NULL) { |
2678 | (*ndpr->ndpr_trace)(ndpr, TRUE); |
2679 | } |
2680 | |
2681 | if (!locked) |
2682 | NDPR_UNLOCK(ndpr); |
2683 | } |
2684 | |
2685 | struct nd_prefix * |
2686 | ndpr_remref(struct nd_prefix *ndpr, int locked) |
2687 | { |
2688 | if (!locked) |
2689 | NDPR_LOCK_SPIN(ndpr); |
2690 | else |
2691 | NDPR_LOCK_ASSERT_HELD(ndpr); |
2692 | |
2693 | if (ndpr->ndpr_refcount == 0) { |
2694 | panic("%s: ndpr %p negative refcnt\n" , __func__, ndpr); |
2695 | /* NOTREACHED */ |
2696 | } else if (ndpr->ndpr_trace != NULL) { |
2697 | (*ndpr->ndpr_trace)(ndpr, FALSE); |
2698 | } |
2699 | |
2700 | if (--ndpr->ndpr_refcount == 0) { |
2701 | if (ndpr->ndpr_addrcnt != 0) { |
2702 | panic("%s: freeing ndpr %p with outstanding address " |
2703 | "reference (%d)" , __func__, ndpr, |
2704 | ndpr->ndpr_addrcnt); |
2705 | /* NOTREACHED */ |
2706 | } |
2707 | NDPR_UNLOCK(ndpr); |
2708 | ndpr_free(ndpr); |
2709 | ndpr = NULL; |
2710 | } |
2711 | |
2712 | if (!locked && ndpr != NULL) |
2713 | NDPR_UNLOCK(ndpr); |
2714 | |
2715 | return (ndpr); |
2716 | } |
2717 | |
2718 | uint64_t |
2719 | ndpr_getexpire(struct nd_prefix *pr) |
2720 | { |
2721 | struct timeval caltime; |
2722 | uint64_t expiry; |
2723 | |
2724 | if (pr->ndpr_expire != 0 && pr->ndpr_vltime != ND6_INFINITE_LIFETIME) { |
2725 | /* account for system time change */ |
2726 | getmicrotime(&caltime); |
2727 | |
2728 | pr->ndpr_base_calendartime += |
2729 | NET_CALCULATE_CLOCKSKEW(caltime, |
2730 | pr->ndpr_base_calendartime, net_uptime(), |
2731 | pr->ndpr_base_uptime); |
2732 | |
2733 | expiry = pr->ndpr_base_calendartime + |
2734 | pr->ndpr_expire - pr->ndpr_base_uptime; |
2735 | } else { |
2736 | expiry = 0; |
2737 | } |
2738 | return (expiry); |
2739 | } |
2740 | |
2741 | /* |
2742 | * A supplement function used in the on-link detection below; |
2743 | * detect if a given prefix has a (probably) reachable advertising router. |
2744 | * XXX: lengthy function name... |
2745 | * |
2746 | * Callers *must* increase the reference count of nd_prefix. |
2747 | */ |
2748 | static struct nd_pfxrouter * |
2749 | find_pfxlist_reachable_router(struct nd_prefix *pr) |
2750 | { |
2751 | struct nd_pfxrouter *pfxrtr; |
2752 | struct rtentry *rt; |
2753 | struct llinfo_nd6 *ln; |
2754 | struct ifnet *ifp; |
2755 | struct in6_addr rtaddr; |
2756 | unsigned int genid; |
2757 | |
2758 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
2759 | NDPR_LOCK_ASSERT_HELD(pr); |
2760 | |
2761 | genid = pr->ndpr_genid; |
2762 | pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); |
2763 | while (pfxrtr) { |
2764 | ifp = pfxrtr->router->ifp; |
2765 | if (pfxrtr->router->stateflags & NDDRF_MAPPED) |
2766 | rtaddr = pfxrtr->router->rtaddr_mapped; |
2767 | else |
2768 | rtaddr = pfxrtr->router->rtaddr; |
2769 | |
2770 | NDPR_UNLOCK(pr); |
2771 | lck_mtx_unlock(nd6_mutex); |
2772 | /* Callee returns a locked route upon success */ |
2773 | if ((rt = nd6_lookup(&rtaddr, 0, ifp, 0)) != NULL) { |
2774 | RT_LOCK_ASSERT_HELD(rt); |
2775 | if ((ln = rt->rt_llinfo) != NULL && |
2776 | ND6_IS_LLINFO_PROBREACH(ln)) { |
2777 | RT_REMREF_LOCKED(rt); |
2778 | RT_UNLOCK(rt); |
2779 | lck_mtx_lock(nd6_mutex); |
2780 | NDPR_LOCK(pr); |
2781 | break; /* found */ |
2782 | } |
2783 | RT_REMREF_LOCKED(rt); |
2784 | RT_UNLOCK(rt); |
2785 | } |
2786 | lck_mtx_lock(nd6_mutex); |
2787 | NDPR_LOCK(pr); |
2788 | if (pr->ndpr_genid != genid) { |
2789 | pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); |
2790 | genid = pr->ndpr_genid; |
2791 | } else |
2792 | pfxrtr = LIST_NEXT(pfxrtr, pfr_entry); |
2793 | } |
2794 | NDPR_LOCK_ASSERT_HELD(pr); |
2795 | |
2796 | return (pfxrtr); |
2797 | |
2798 | } |
2799 | |
2800 | /* |
2801 | * Check if each prefix in the prefix list has at least one available router |
2802 | * that advertised the prefix (a router is "available" if its neighbor cache |
2803 | * entry is reachable or probably reachable). |
2804 | * If the check fails, the prefix may be off-link, because, for example, |
2805 | * we have moved from the network but the lifetime of the prefix has not |
2806 | * expired yet. So we should not use the prefix if there is another prefix |
2807 | * that has an available router. |
2808 | * But, if there is no prefix that has an available router, we still regards |
2809 | * all the prefixes as on-link. This is because we can't tell if all the |
2810 | * routers are simply dead or if we really moved from the network and there |
2811 | * is no router around us. |
2812 | */ |
2813 | void |
2814 | pfxlist_onlink_check(void) |
2815 | { |
2816 | struct nd_prefix *pr, *prclear; |
2817 | struct in6_ifaddr *ifa; |
2818 | struct nd_defrouter *dr; |
2819 | struct nd_pfxrouter *pfxrtr = NULL; |
2820 | int err, i, found = 0; |
2821 | struct ifaddr **ifap = NULL; |
2822 | struct nd_prefix *ndpr; |
2823 | |
2824 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
2825 | |
2826 | while (nd_prefix_busy) { |
2827 | nd_prefix_waiters++; |
2828 | msleep(nd_prefix_waitchan, nd6_mutex, (PZERO-1), |
2829 | __func__, NULL); |
2830 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
2831 | } |
2832 | nd_prefix_busy = TRUE; |
2833 | |
2834 | /* |
2835 | * Check if there is a prefix that has a reachable advertising |
2836 | * router. |
2837 | */ |
2838 | pr = nd_prefix.lh_first; |
2839 | while (pr) { |
2840 | NDPR_LOCK(pr); |
2841 | if (pr->ndpr_stateflags & NDPRF_PROCESSED_ONLINK) { |
2842 | NDPR_UNLOCK(pr); |
2843 | pr = pr->ndpr_next; |
2844 | continue; |
2845 | } |
2846 | NDPR_ADDREF_LOCKED(pr); |
2847 | if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr) && |
2848 | (pr->ndpr_debug & IFD_ATTACHED)) { |
2849 | if (NDPR_REMREF_LOCKED(pr) == NULL) |
2850 | pr = NULL; |
2851 | else |
2852 | NDPR_UNLOCK(pr); |
2853 | break; |
2854 | } |
2855 | pr->ndpr_stateflags |= NDPRF_PROCESSED_ONLINK; |
2856 | NDPR_UNLOCK(pr); |
2857 | NDPR_REMREF(pr); |
2858 | /* |
2859 | * Since find_pfxlist_reachable_router() drops the nd6_mutex, we |
2860 | * have to start over, but the NDPRF_PROCESSED_ONLINK flag will |
2861 | * stop us from checking the same prefix twice. |
2862 | */ |
2863 | pr = nd_prefix.lh_first; |
2864 | } |
2865 | LIST_FOREACH(prclear, &nd_prefix, ndpr_entry) { |
2866 | NDPR_LOCK(prclear); |
2867 | prclear->ndpr_stateflags &= ~NDPRF_PROCESSED_ONLINK; |
2868 | NDPR_UNLOCK(prclear); |
2869 | } |
2870 | /* |
2871 | * If we have no such prefix, check whether we still have a router |
2872 | * that does not advertise any prefixes. |
2873 | */ |
2874 | if (pr == NULL) { |
2875 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
2876 | dr = TAILQ_NEXT(dr, dr_entry)) { |
2877 | struct nd_prefix *pr0; |
2878 | |
2879 | for (pr0 = nd_prefix.lh_first; pr0; |
2880 | pr0 = pr0->ndpr_next) { |
2881 | NDPR_LOCK(pr0); |
2882 | if ((pfxrtr = pfxrtr_lookup(pr0, dr)) != NULL) { |
2883 | NDPR_UNLOCK(pr0); |
2884 | break; |
2885 | } |
2886 | NDPR_UNLOCK(pr0); |
2887 | } |
2888 | if (pfxrtr != NULL) |
2889 | break; |
2890 | } |
2891 | } |
2892 | if (pr != NULL || (TAILQ_FIRST(&nd_defrouter) && pfxrtr == NULL)) { |
2893 | /* |
2894 | * There is at least one prefix that has a reachable router, |
2895 | * or at least a router which probably does not advertise |
2896 | * any prefixes. The latter would be the case when we move |
2897 | * to a new link where we have a router that does not provide |
2898 | * prefixes and we configure an address by hand. |
2899 | * Detach prefixes which have no reachable advertising |
2900 | * router, and attach other prefixes. |
2901 | */ |
2902 | pr = nd_prefix.lh_first; |
2903 | while (pr) { |
2904 | NDPR_LOCK(pr); |
2905 | /* |
2906 | * We aren't interested prefixes already processed, |
2907 | * nor in prefixes without the L bit |
2908 | * set nor in static prefixes |
2909 | */ |
2910 | if (pr->ndpr_raf_onlink == 0 || |
2911 | pr->ndpr_stateflags & NDPRF_PROCESSED_ONLINK || |
2912 | pr->ndpr_stateflags & NDPRF_STATIC) { |
2913 | NDPR_UNLOCK(pr); |
2914 | pr = pr->ndpr_next; |
2915 | continue; |
2916 | } |
2917 | NDPR_ADDREF_LOCKED(pr); |
2918 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && |
2919 | find_pfxlist_reachable_router(pr) == NULL && |
2920 | (pr->ndpr_debug & IFD_ATTACHED)) |
2921 | pr->ndpr_stateflags |= NDPRF_DETACHED; |
2922 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && |
2923 | find_pfxlist_reachable_router(pr) != NULL && |
2924 | (pr->ndpr_debug & IFD_ATTACHED)) |
2925 | pr->ndpr_stateflags &= ~NDPRF_DETACHED; |
2926 | pr->ndpr_stateflags |= NDPRF_PROCESSED_ONLINK; |
2927 | NDPR_UNLOCK(pr); |
2928 | NDPR_REMREF(pr); |
2929 | /* |
2930 | * Since find_pfxlist_reachable_router() drops the |
2931 | * nd6_mutex, we have to start over, but the |
2932 | * NDPRF_PROCESSED_ONLINK flag will stop us from |
2933 | * checking the same prefix twice. |
2934 | */ |
2935 | pr = nd_prefix.lh_first; |
2936 | } |
2937 | } else { |
2938 | /* there is no prefix that has a reachable router */ |
2939 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { |
2940 | NDPR_LOCK(pr); |
2941 | if (pr->ndpr_raf_onlink == 0 || |
2942 | pr->ndpr_stateflags & NDPRF_STATIC) { |
2943 | NDPR_UNLOCK(pr); |
2944 | continue; |
2945 | } |
2946 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) |
2947 | pr->ndpr_stateflags &= ~NDPRF_DETACHED; |
2948 | NDPR_UNLOCK(pr); |
2949 | } |
2950 | } |
2951 | LIST_FOREACH(prclear, &nd_prefix, ndpr_entry) { |
2952 | NDPR_LOCK(prclear); |
2953 | prclear->ndpr_stateflags &= ~NDPRF_PROCESSED_ONLINK; |
2954 | NDPR_UNLOCK(prclear); |
2955 | } |
2956 | /* |
2957 | * Remove each interface route associated with a (just) detached |
2958 | * prefix, and reinstall the interface route for a (just) attached |
2959 | * prefix. Note that all attempt of reinstallation does not |
2960 | * necessarily success, when a same prefix is shared among multiple |
2961 | * interfaces. Such cases will be handled in nd6_prefix_onlink, |
2962 | * so we don't have to care about them. |
2963 | */ |
2964 | pr = nd_prefix.lh_first; |
2965 | while (pr) { |
2966 | int e; |
2967 | |
2968 | NDPR_LOCK(pr); |
2969 | if (pr->ndpr_raf_onlink == 0 || |
2970 | pr->ndpr_stateflags & NDPRF_STATIC || |
2971 | pr->ndpr_stateflags & NDPRF_PROCESSED_ONLINK || |
2972 | pr->ndpr_stateflags & NDPRF_DEFUNCT) { |
2973 | NDPR_UNLOCK(pr); |
2974 | pr = pr->ndpr_next; |
2975 | continue; |
2976 | } |
2977 | pr->ndpr_stateflags |= NDPRF_PROCESSED_ONLINK; |
2978 | NDPR_ADDREF_LOCKED(pr); |
2979 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && |
2980 | (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { |
2981 | NDPR_UNLOCK(pr); |
2982 | lck_mtx_unlock(nd6_mutex); |
2983 | if ((e = nd6_prefix_offlink(pr)) != 0) { |
2984 | nd6log((LOG_ERR, |
2985 | "pfxlist_onlink_check: failed to " |
2986 | "make %s/%d offlink, errno=%d\n" , |
2987 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
2988 | pr->ndpr_plen, e)); |
2989 | } |
2990 | lck_mtx_lock(nd6_mutex); |
2991 | NDPR_REMREF(pr); |
2992 | pr = nd_prefix.lh_first; |
2993 | continue; |
2994 | } |
2995 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && |
2996 | (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && |
2997 | pr->ndpr_raf_onlink) { |
2998 | NDPR_UNLOCK(pr); |
2999 | if ((e = nd6_prefix_onlink(pr)) != 0) { |
3000 | nd6log((LOG_ERR, |
3001 | "pfxlist_onlink_check: failed to " |
3002 | "make %s/%d offlink, errno=%d\n" , |
3003 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3004 | pr->ndpr_plen, e)); |
3005 | } |
3006 | NDPR_REMREF(pr); |
3007 | pr = nd_prefix.lh_first; |
3008 | continue; |
3009 | } else { |
3010 | NDPR_UNLOCK(pr); |
3011 | } |
3012 | NDPR_REMREF(pr); |
3013 | pr = pr->ndpr_next; |
3014 | } |
3015 | LIST_FOREACH(prclear, &nd_prefix, ndpr_entry) { |
3016 | NDPR_LOCK(prclear); |
3017 | prclear->ndpr_stateflags &= ~NDPRF_PROCESSED_ONLINK; |
3018 | NDPR_UNLOCK(prclear); |
3019 | } |
3020 | VERIFY(nd_prefix_busy); |
3021 | nd_prefix_busy = FALSE; |
3022 | if (nd_prefix_waiters > 0) { |
3023 | nd_prefix_waiters = 0; |
3024 | wakeup(nd_prefix_waitchan); |
3025 | } |
3026 | |
3027 | /* |
3028 | * Changes on the prefix status might affect address status as well. |
3029 | * Make sure that all addresses derived from an attached prefix are |
3030 | * attached, and that all addresses derived from a detached prefix are |
3031 | * detached. Note, however, that a manually configured address should |
3032 | * always be attached. |
3033 | * The precise detection logic is same as the one for prefixes. |
3034 | * |
3035 | * ifnet_get_address_list_family_internal() may fail due to memory |
3036 | * pressure, but we will eventually be called again when we receive |
3037 | * another NA, RA, or when the link status changes. |
3038 | */ |
3039 | err = ifnet_get_address_list_family_internal(NULL, &ifap, AF_INET6, 0, |
3040 | M_NOWAIT, 0); |
3041 | if (err != 0 || ifap == NULL) { |
3042 | nd6log((LOG_ERR, "%s: ifnet_get_address_list_family_internal " |
3043 | "failed" , __func__)); |
3044 | return; |
3045 | } |
3046 | for (i = 0; ifap[i]; i++) { |
3047 | ifa = ifatoia6(ifap[i]); |
3048 | IFA_LOCK(&ifa->ia_ifa); |
3049 | if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0 || |
3050 | (ifap[i]->ifa_debug & IFD_ATTACHED) == 0) { |
3051 | IFA_UNLOCK(&ifa->ia_ifa); |
3052 | continue; |
3053 | } |
3054 | if ((ndpr = ifa->ia6_ndpr) == NULL) { |
3055 | /* |
3056 | * This can happen when we first configure the address |
3057 | * (i.e. the address exists, but the prefix does not). |
3058 | * XXX: complicated relationships... |
3059 | */ |
3060 | IFA_UNLOCK(&ifa->ia_ifa); |
3061 | continue; |
3062 | } |
3063 | IFA_UNLOCK(&ifa->ia_ifa); |
3064 | |
3065 | NDPR_LOCK(ndpr); |
3066 | NDPR_ADDREF_LOCKED(ndpr); |
3067 | if (find_pfxlist_reachable_router(ndpr)) { |
3068 | if (NDPR_REMREF_LOCKED(ndpr) == NULL) { |
3069 | found = 0; |
3070 | } else { |
3071 | NDPR_UNLOCK(ndpr); |
3072 | found = 1; |
3073 | } |
3074 | break; |
3075 | } |
3076 | NDPR_UNLOCK(ndpr); |
3077 | NDPR_REMREF(ndpr); |
3078 | } |
3079 | if (found) { |
3080 | for (i = 0; ifap[i]; i++) { |
3081 | ifa = ifatoia6(ifap[i]); |
3082 | IFA_LOCK(&ifa->ia_ifa); |
3083 | if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0 || |
3084 | (ifap[i]->ifa_debug & IFD_ATTACHED) == 0) { |
3085 | IFA_UNLOCK(&ifa->ia_ifa); |
3086 | continue; |
3087 | } |
3088 | if ((ndpr = ifa->ia6_ndpr) == NULL) { |
3089 | /* XXX: see above. */ |
3090 | IFA_UNLOCK(&ifa->ia_ifa); |
3091 | continue; |
3092 | } |
3093 | IFA_UNLOCK(&ifa->ia_ifa); |
3094 | NDPR_LOCK(ndpr); |
3095 | NDPR_ADDREF_LOCKED(ndpr); |
3096 | if (find_pfxlist_reachable_router(ndpr)) { |
3097 | NDPR_UNLOCK(ndpr); |
3098 | IFA_LOCK(&ifa->ia_ifa); |
3099 | if (ifa->ia6_flags & IN6_IFF_DETACHED) { |
3100 | ifa->ia6_flags &= ~IN6_IFF_DETACHED; |
3101 | in6_ifaddr_set_dadprogress((struct in6_ifaddr *)ifa); |
3102 | IFA_UNLOCK(&ifa->ia_ifa); |
3103 | nd6_dad_start((struct ifaddr *)ifa, 0); |
3104 | } else { |
3105 | IFA_UNLOCK(&ifa->ia_ifa); |
3106 | } |
3107 | } else { |
3108 | NDPR_UNLOCK(ndpr); |
3109 | IFA_LOCK(&ifa->ia_ifa); |
3110 | if ((ifa->ia6_flags & IN6_IFF_DETACHED) == 0) { |
3111 | ifa->ia6_flags |= IN6_IFF_DETACHED; |
3112 | in6_event_enqueue_nwk_wq_entry(IN6_ADDR_MARKED_DETACHED, |
3113 | ifa->ia_ifa.ifa_ifp, &ifa->ia_addr.sin6_addr, |
3114 | 0); |
3115 | } |
3116 | IFA_UNLOCK(&ifa->ia_ifa); |
3117 | } |
3118 | NDPR_REMREF(ndpr); |
3119 | } |
3120 | } else { |
3121 | for (i = 0; ifap[i]; i++) { |
3122 | ifa = ifatoia6(ifap[i]); |
3123 | IFA_LOCK(&ifa->ia_ifa); |
3124 | if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) { |
3125 | IFA_UNLOCK(&ifa->ia_ifa); |
3126 | continue; |
3127 | } |
3128 | if (ifa->ia6_flags & IN6_IFF_DETACHED) { |
3129 | ifa->ia6_flags &= ~IN6_IFF_DETACHED; |
3130 | in6_ifaddr_set_dadprogress((struct in6_ifaddr *)ifa); |
3131 | IFA_UNLOCK(&ifa->ia_ifa); |
3132 | /* Do we need a delay in this case? */ |
3133 | nd6_dad_start((struct ifaddr *)ifa, 0); |
3134 | } else { |
3135 | IFA_UNLOCK(&ifa->ia_ifa); |
3136 | } |
3137 | } |
3138 | } |
3139 | ifnet_free_address_list(ifap); |
3140 | } |
3141 | |
3142 | static struct nd_prefix * |
3143 | nd6_prefix_equal_lookup(struct nd_prefix *pr, boolean_t primary_only) |
3144 | { |
3145 | struct nd_prefix *opr; |
3146 | |
3147 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
3148 | |
3149 | for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { |
3150 | if (opr == pr) |
3151 | continue; |
3152 | |
3153 | NDPR_LOCK(opr); |
3154 | if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) { |
3155 | NDPR_UNLOCK(opr); |
3156 | continue; |
3157 | } |
3158 | if (opr->ndpr_plen == pr->ndpr_plen && |
3159 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, |
3160 | &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen) && |
3161 | (!primary_only || |
3162 | !(opr->ndpr_stateflags & NDPRF_IFSCOPE))) { |
3163 | NDPR_ADDREF_LOCKED(opr); |
3164 | NDPR_UNLOCK(opr); |
3165 | return (opr); |
3166 | } |
3167 | NDPR_UNLOCK(opr); |
3168 | } |
3169 | return (NULL); |
3170 | } |
3171 | |
3172 | /* |
3173 | * Synchronize the interface routes of similar prefixes on different |
3174 | * interfaces; the one using the default interface would be (re)installed |
3175 | * as a primary/non-scoped entry, and the rest as scoped entri(es). |
3176 | */ |
3177 | static void |
3178 | nd6_prefix_sync(struct ifnet *ifp) |
3179 | { |
3180 | struct nd_prefix *pr, *opr; |
3181 | int err = 0; |
3182 | |
3183 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
3184 | |
3185 | if (ifp == NULL) |
3186 | return; |
3187 | |
3188 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { |
3189 | NDPR_LOCK(pr); |
3190 | if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) { |
3191 | NDPR_UNLOCK(pr); |
3192 | continue; |
3193 | } |
3194 | if (pr->ndpr_ifp == ifp && |
3195 | (pr->ndpr_stateflags & NDPRF_IFSCOPE) && |
3196 | !IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) { |
3197 | NDPR_UNLOCK(pr); |
3198 | break; |
3199 | } |
3200 | NDPR_UNLOCK(pr); |
3201 | } |
3202 | |
3203 | if (pr == NULL) |
3204 | return; |
3205 | |
3206 | /* Remove conflicting entries */ |
3207 | opr = nd6_prefix_equal_lookup(pr, TRUE); |
3208 | if (opr != NULL) { |
3209 | lck_mtx_unlock(nd6_mutex); |
3210 | err = nd6_prefix_offlink(opr); |
3211 | lck_mtx_lock(nd6_mutex); |
3212 | if (err != 0) { |
3213 | nd6log((LOG_ERR, |
3214 | "%s: failed to make %s/%d offlink on %s, " |
3215 | "errno=%d\n" , __func__, |
3216 | ip6_sprintf(&opr->ndpr_prefix.sin6_addr), |
3217 | opr->ndpr_plen, if_name(opr->ndpr_ifp), err)); |
3218 | } |
3219 | } else { |
3220 | nd6log((LOG_ERR, |
3221 | "%s: scoped %s/%d on %s has no matching unscoped prefix\n" , |
3222 | __func__, ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3223 | pr->ndpr_plen, if_name(pr->ndpr_ifp))); |
3224 | } |
3225 | |
3226 | lck_mtx_unlock(nd6_mutex); |
3227 | err = nd6_prefix_offlink(pr); |
3228 | lck_mtx_lock(nd6_mutex); |
3229 | if (err != 0) { |
3230 | nd6log((LOG_ERR, |
3231 | "%s: failed to make %s/%d offlink on %s, errno=%d\n" , |
3232 | __func__, ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3233 | pr->ndpr_plen, if_name(pr->ndpr_ifp), err)); |
3234 | } |
3235 | |
3236 | /* Add the entries back */ |
3237 | if (opr != NULL) { |
3238 | err = nd6_prefix_onlink_scoped(opr, opr->ndpr_ifp->if_index); |
3239 | if (err != 0) { |
3240 | nd6log((LOG_ERR, |
3241 | "%s: failed to make %s/%d scoped onlink on %s, " |
3242 | "errno=%d\n" , __func__, |
3243 | ip6_sprintf(&opr->ndpr_prefix.sin6_addr), |
3244 | opr->ndpr_plen, if_name(opr->ndpr_ifp), err)); |
3245 | } |
3246 | } |
3247 | |
3248 | err = nd6_prefix_onlink_scoped(pr, IFSCOPE_NONE); |
3249 | if (err != 0) { |
3250 | nd6log((LOG_ERR, |
3251 | "%s: failed to make %s/%d onlink on %s, errno=%d\n" , |
3252 | __func__, ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3253 | pr->ndpr_plen, if_name(pr->ndpr_ifp), err)); |
3254 | } |
3255 | |
3256 | if (err != 0) { |
3257 | nd6log((LOG_ERR, |
3258 | "%s: error promoting %s/%d to %s from %s\n" , |
3259 | __func__, ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3260 | pr->ndpr_plen, if_name(pr->ndpr_ifp), |
3261 | (opr != NULL) ? if_name(opr->ndpr_ifp) : "NONE" )); |
3262 | } else { |
3263 | nd6log2((LOG_INFO, |
3264 | "%s: %s/%d promoted, previously on %s\n" , |
3265 | if_name(pr->ndpr_ifp), |
3266 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, |
3267 | (opr != NULL) ? if_name(opr->ndpr_ifp) : "NONE" )); |
3268 | } |
3269 | |
3270 | if (opr != NULL) |
3271 | NDPR_REMREF(opr); |
3272 | } |
3273 | |
3274 | static int |
3275 | nd6_prefix_onlink_common(struct nd_prefix *pr, boolean_t force_scoped, |
3276 | unsigned int ifscope) |
3277 | { |
3278 | struct ifaddr *ifa; |
3279 | struct ifnet *ifp = pr->ndpr_ifp; |
3280 | struct sockaddr_in6 mask6, prefix; |
3281 | struct nd_prefix *opr; |
3282 | u_int32_t rtflags; |
3283 | int error = 0, prproxy = 0; |
3284 | struct rtentry *rt = NULL; |
3285 | |
3286 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
3287 | |
3288 | /* sanity check */ |
3289 | NDPR_LOCK(pr); |
3290 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { |
3291 | nd6log((LOG_ERR, |
3292 | "%s: %s/%d on %s scoped=%d is already on-link\n" , |
3293 | __func__, ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3294 | pr->ndpr_plen, if_name(pr->ndpr_ifp), |
3295 | (pr->ndpr_stateflags & NDPRF_IFSCOPE) ? 1 : 0); |
3296 | NDPR_UNLOCK(pr); |
3297 | return (EEXIST)); |
3298 | } |
3299 | NDPR_UNLOCK(pr); |
3300 | |
3301 | /* |
3302 | * Add the interface route associated with the prefix. Before |
3303 | * installing the route, check if there's the same prefix on another |
3304 | * interface, and the prefix has already installed the interface route. |
3305 | */ |
3306 | opr = nd6_prefix_equal_lookup(pr, FALSE); |
3307 | if (opr != NULL) |
3308 | NDPR_REMREF(opr); |
3309 | |
3310 | if (!force_scoped) { |
3311 | /* |
3312 | * If a primary/non-scoped interface route already exists, |
3313 | * install the new one as a scoped entry. If the existing |
3314 | * interface route is scoped, install new as non-scoped. |
3315 | */ |
3316 | ifscope = (opr != NULL) ? ifp->if_index : IFSCOPE_NONE; |
3317 | opr = nd6_prefix_equal_lookup(pr, TRUE); |
3318 | if (opr != NULL) |
3319 | NDPR_REMREF(opr); |
3320 | else if (ifscope != IFSCOPE_NONE) |
3321 | ifscope = IFSCOPE_NONE; |
3322 | } |
3323 | |
3324 | /* |
3325 | * We prefer link-local addresses as the associated interface address. |
3326 | */ |
3327 | /* search for a link-local addr */ |
3328 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, |
3329 | IN6_IFF_NOTREADY | IN6_IFF_ANYCAST); |
3330 | if (ifa == NULL) { |
3331 | struct in6_ifaddr *ia6; |
3332 | ifnet_lock_shared(ifp); |
3333 | IFP_TO_IA6(ifp, ia6); |
3334 | ifnet_lock_done(ifp); |
3335 | if (ia6 != NULL) |
3336 | ifa = &ia6->ia_ifa; |
3337 | /* should we care about ia6_flags? */ |
3338 | } |
3339 | NDPR_LOCK(pr); |
3340 | if (ifa == NULL) { |
3341 | /* |
3342 | * This can still happen, when, for example, we receive an RA |
3343 | * containing a prefix with the L bit set and the A bit clear, |
3344 | * after removing all IPv6 addresses on the receiving |
3345 | * interface. This should, of course, be rare though. |
3346 | */ |
3347 | nd6log((LOG_NOTICE, |
3348 | "nd6_prefix_onlink: failed to find any ifaddr" |
3349 | " to add route for a prefix(%s/%d) on %s\n" , |
3350 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3351 | pr->ndpr_plen, if_name(ifp))); |
3352 | NDPR_UNLOCK(pr); |
3353 | return (0); |
3354 | } |
3355 | |
3356 | /* |
3357 | * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. |
3358 | * ifa->ifa_rtrequest = nd6_rtrequest; |
3359 | */ |
3360 | bzero(&mask6, sizeof (mask6)); |
3361 | mask6.sin6_len = sizeof (mask6); |
3362 | mask6.sin6_addr = pr->ndpr_mask; |
3363 | prefix = pr->ndpr_prefix; |
3364 | if ((rt = pr->ndpr_rt) != NULL) |
3365 | pr->ndpr_rt = NULL; |
3366 | NDPR_ADDREF_LOCKED(pr); /* keep reference for this routine */ |
3367 | NDPR_UNLOCK(pr); |
3368 | |
3369 | IFA_LOCK_SPIN(ifa); |
3370 | rtflags = ifa->ifa_flags | RTF_CLONING | RTF_UP; |
3371 | IFA_UNLOCK(ifa); |
3372 | if (nd6_need_cache(ifp)) { |
3373 | /* explicitly set in case ifa_flags does not set the flag. */ |
3374 | rtflags |= RTF_CLONING; |
3375 | } else { |
3376 | /* |
3377 | * explicitly clear the cloning bit in case ifa_flags sets it. |
3378 | */ |
3379 | rtflags &= ~RTF_CLONING; |
3380 | } |
3381 | |
3382 | lck_mtx_unlock(nd6_mutex); |
3383 | |
3384 | if (rt != NULL) { |
3385 | rtfree(rt); |
3386 | rt = NULL; |
3387 | } |
3388 | |
3389 | error = rtrequest_scoped(RTM_ADD, (struct sockaddr *)&prefix, |
3390 | ifa->ifa_addr, (struct sockaddr *)&mask6, rtflags, &rt, |
3391 | ifscope); |
3392 | |
3393 | /* |
3394 | * Serialize the setting of NDPRF_PRPROXY. |
3395 | */ |
3396 | lck_mtx_lock(&proxy6_lock); |
3397 | |
3398 | if (rt != NULL) { |
3399 | RT_LOCK(rt); |
3400 | nd6_rtmsg(RTM_ADD, rt); |
3401 | RT_UNLOCK(rt); |
3402 | NDPR_LOCK(pr); |
3403 | } else { |
3404 | NDPR_LOCK(pr); |
3405 | nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a" |
3406 | " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx," |
3407 | " scoped=%d, errno = %d\n" , |
3408 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3409 | pr->ndpr_plen, if_name(ifp), |
3410 | ip6_sprintf(&((struct sockaddr_in6 *) |
3411 | (void *)ifa->ifa_addr)->sin6_addr), |
3412 | ip6_sprintf(&mask6.sin6_addr), rtflags, |
3413 | (ifscope != IFSCOPE_NONE), error)); |
3414 | } |
3415 | NDPR_LOCK_ASSERT_HELD(pr); |
3416 | |
3417 | pr->ndpr_stateflags &= ~(NDPRF_IFSCOPE | NDPRF_PRPROXY); |
3418 | |
3419 | /* |
3420 | * TODO: If the prefix route exists, we should really find it and |
3421 | * refer the prefix to it; otherwise ndpr_rt is NULL. |
3422 | */ |
3423 | if (!(pr->ndpr_stateflags & NDPRF_DEFUNCT) && |
3424 | (rt != NULL || error == EEXIST)) { |
3425 | struct nd_ifinfo *ndi = NULL; |
3426 | |
3427 | VERIFY(pr->ndpr_prproxy_sols_cnt == 0); |
3428 | VERIFY(RB_EMPTY(&pr->ndpr_prproxy_sols)); |
3429 | |
3430 | ndi = ND_IFINFO(ifp); |
3431 | VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); |
3432 | lck_mtx_lock(&ndi->lock); |
3433 | |
3434 | pr->ndpr_rt = rt; /* keep reference from rtrequest */ |
3435 | pr->ndpr_stateflags |= NDPRF_ONLINK; |
3436 | if (ifscope != IFSCOPE_NONE) { |
3437 | pr->ndpr_stateflags |= NDPRF_IFSCOPE; |
3438 | } else if ((rtflags & RTF_CLONING) && |
3439 | (ndi->flags & ND6_IFF_PROXY_PREFIXES) && |
3440 | !IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) { |
3441 | /* |
3442 | * At present, in order for the prefix to be eligible |
3443 | * as a proxying/proxied prefix, we require that the |
3444 | * prefix route entry be marked as a cloning route with |
3445 | * RTF_PROXY; i.e. nd6_need_cache() needs to return |
3446 | * true for the interface type, hence the test for |
3447 | * RTF_CLONING above. |
3448 | */ |
3449 | pr->ndpr_stateflags |= NDPRF_PRPROXY; |
3450 | } |
3451 | |
3452 | lck_mtx_unlock(&ndi->lock); |
3453 | } else if (rt != NULL && pr->ndpr_stateflags & NDPRF_DEFUNCT) |
3454 | rtfree(rt); |
3455 | |
3456 | prproxy = (pr->ndpr_stateflags & NDPRF_PRPROXY); |
3457 | VERIFY(!prproxy || !(pr->ndpr_stateflags & NDPRF_IFSCOPE)); |
3458 | NDPR_UNLOCK(pr); |
3459 | |
3460 | IFA_REMREF(ifa); |
3461 | |
3462 | /* |
3463 | * If this is an upstream prefix, find the downstream ones (if any) |
3464 | * and re-configure their prefix routes accordingly. Otherwise, |
3465 | * this could be potentially be a downstream prefix, and so find the |
3466 | * upstream prefix, if any. |
3467 | */ |
3468 | nd6_prproxy_prelist_update(pr, prproxy ? pr : NULL); |
3469 | |
3470 | NDPR_REMREF(pr); /* release reference for this routine */ |
3471 | lck_mtx_unlock(&proxy6_lock); |
3472 | |
3473 | lck_mtx_lock(nd6_mutex); |
3474 | |
3475 | return (error); |
3476 | } |
3477 | |
3478 | int |
3479 | nd6_prefix_onlink(struct nd_prefix *pr) |
3480 | { |
3481 | return (nd6_prefix_onlink_common(pr, FALSE, IFSCOPE_NONE)); |
3482 | } |
3483 | |
3484 | int |
3485 | nd6_prefix_onlink_scoped(struct nd_prefix *pr, unsigned int ifscope) |
3486 | { |
3487 | return (nd6_prefix_onlink_common(pr, TRUE, ifscope)); |
3488 | } |
3489 | |
3490 | int |
3491 | nd6_prefix_offlink(struct nd_prefix *pr) |
3492 | { |
3493 | int plen, error = 0, prproxy; |
3494 | struct ifnet *ifp = pr->ndpr_ifp; |
3495 | struct sockaddr_in6 sa6, mask6, prefix; |
3496 | struct rtentry *rt = NULL, *ndpr_rt = NULL; |
3497 | unsigned int ifscope; |
3498 | |
3499 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); |
3500 | |
3501 | /* sanity check */ |
3502 | NDPR_LOCK(pr); |
3503 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { |
3504 | nd6log((LOG_ERR, |
3505 | "nd6_prefix_offlink: %s/%d on %s scoped=%d is already " |
3506 | "off-link\n" , ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
3507 | pr->ndpr_plen, if_name(pr->ndpr_ifp), |
3508 | (pr->ndpr_stateflags & NDPRF_IFSCOPE) ? 1 : 0)); |
3509 | NDPR_UNLOCK(pr); |
3510 | return (EEXIST); |
3511 | } |
3512 | |
3513 | bzero(&sa6, sizeof (sa6)); |
3514 | sa6.sin6_family = AF_INET6; |
3515 | sa6.sin6_len = sizeof (sa6); |
3516 | bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, |
3517 | sizeof (struct in6_addr)); |
3518 | bzero(&mask6, sizeof (mask6)); |
3519 | mask6.sin6_family = AF_INET6; |
3520 | mask6.sin6_len = sizeof (sa6); |
3521 | bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof (struct in6_addr)); |
3522 | prefix = pr->ndpr_prefix; |
3523 | plen = pr->ndpr_plen; |
3524 | if ((ndpr_rt = pr->ndpr_rt) != NULL) |
3525 | pr->ndpr_rt = NULL; |
3526 | NDPR_ADDREF_LOCKED(pr); /* keep reference for this routine */ |
3527 | NDPR_UNLOCK(pr); |
3528 | |
3529 | ifscope = (pr->ndpr_stateflags & NDPRF_IFSCOPE) ? |
3530 | ifp->if_index : IFSCOPE_NONE; |
3531 | |
3532 | error = rtrequest_scoped(RTM_DELETE, (struct sockaddr *)&sa6, |
3533 | NULL, (struct sockaddr *)&mask6, 0, &rt, ifscope); |
3534 | |
3535 | if (rt != NULL) { |
3536 | /* report the route deletion to the routing socket. */ |
3537 | RT_LOCK(rt); |
3538 | nd6_rtmsg(RTM_DELETE, rt); |
3539 | RT_UNLOCK(rt); |
3540 | rtfree(rt); |
3541 | |
3542 | } else { |
3543 | nd6log((LOG_ERR, |
3544 | "nd6_prefix_offlink: failed to delete route: " |
3545 | "%s/%d on %s, scoped %d, (errno = %d)\n" , |
3546 | ip6_sprintf(&sa6.sin6_addr), plen, if_name(ifp), |
3547 | (ifscope != IFSCOPE_NONE), error)); |
3548 | } |
3549 | |
3550 | if (ndpr_rt != NULL) |
3551 | rtfree(ndpr_rt); |
3552 | |
3553 | lck_mtx_lock(&proxy6_lock); |
3554 | |
3555 | NDPR_LOCK(pr); |
3556 | prproxy = (pr->ndpr_stateflags & NDPRF_PRPROXY); |
3557 | VERIFY(!prproxy || !(pr->ndpr_stateflags & NDPRF_IFSCOPE)); |
3558 | pr->ndpr_stateflags &= ~(NDPRF_ONLINK | NDPRF_IFSCOPE | NDPRF_PRPROXY); |
3559 | if (pr->ndpr_prproxy_sols_cnt > 0) { |
3560 | VERIFY(prproxy); |
3561 | nd6_prproxy_sols_reap(pr); |
3562 | VERIFY(pr->ndpr_prproxy_sols_cnt == 0); |
3563 | VERIFY(RB_EMPTY(&pr->ndpr_prproxy_sols)); |
3564 | } |
3565 | NDPR_UNLOCK(pr); |
3566 | |
3567 | /* |
3568 | * If this was an upstream prefix, find the downstream ones and do |
3569 | * some cleanups. If this was a downstream prefix, the prefix route |
3570 | * has been removed from the routing table above, but there may be |
3571 | * other tasks to perform. |
3572 | */ |
3573 | nd6_prproxy_prelist_update(pr, prproxy ? pr : NULL); |
3574 | |
3575 | NDPR_REMREF(pr); /* release reference for this routine */ |
3576 | lck_mtx_unlock(&proxy6_lock); |
3577 | |
3578 | return (error); |
3579 | } |
3580 | |
3581 | struct in6_ifaddr * |
3582 | in6_pfx_newpersistaddr(struct nd_prefix *pr, int mcast, int *errorp, boolean_t is_clat46) |
3583 | { |
3584 | struct in6_ifaddr *ia6 = NULL; |
3585 | struct ifnet *ifp = NULL; |
3586 | struct nd_ifinfo *ndi = NULL; |
3587 | struct in6_addr mask; |
3588 | struct in6_aliasreq ifra; |
3589 | int error, ifaupdate, iidlen, notcga; |
3590 | |
3591 | VERIFY(pr != NULL); |
3592 | VERIFY(errorp != NULL); |
3593 | |
3594 | NDPR_LOCK(pr); |
3595 | ifp = pr->ndpr_ifp; |
3596 | ia6 = NULL; |
3597 | error = 0; |
3598 | |
3599 | /* |
3600 | * Prefix Length check: |
3601 | * If the sum of the prefix length and interface identifier |
3602 | * length does not equal 128 bits, the Prefix Information |
3603 | * option MUST be ignored. The length of the interface |
3604 | * identifier is defined in a separate link-type specific |
3605 | * document. |
3606 | */ |
3607 | iidlen = in6_if2idlen(ifp); |
3608 | if (iidlen < 0) { |
3609 | error = EADDRNOTAVAIL; |
3610 | /* this should not happen, so we always log it. */ |
3611 | log(LOG_ERR, "%s: IID length undefined (%s)\n" , |
3612 | __func__, if_name(ifp)); |
3613 | goto unlock1; |
3614 | } else if (iidlen != 64) { |
3615 | error = EADDRNOTAVAIL; |
3616 | /* |
3617 | * stateless autoconfiguration not yet well-defined for IID |
3618 | * lengths other than 64 octets. Just give up for now. |
3619 | */ |
3620 | nd6log((LOG_INFO, "%s: IID length not 64 octets (%s)\n" , |
3621 | __func__, if_name(ifp))); |
3622 | goto unlock1; |
3623 | } |
3624 | |
3625 | if (iidlen + pr->ndpr_plen != 128) { |
3626 | error = EADDRNOTAVAIL; |
3627 | nd6log((LOG_INFO, |
3628 | "%s: invalid prefix length %d for %s, ignored\n" , |
3629 | __func__, pr->ndpr_plen, if_name(ifp))); |
3630 | goto unlock1; |
3631 | } |
3632 | |
3633 | bzero(&ifra, sizeof (ifra)); |
3634 | strlcpy(ifra.ifra_name, if_name(ifp), sizeof (ifra.ifra_name)); |
3635 | ifra.ifra_addr.sin6_family = AF_INET6; |
3636 | ifra.ifra_addr.sin6_len = sizeof (struct sockaddr_in6); |
3637 | |
3638 | /* prefix */ |
3639 | bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr, |
3640 | sizeof (ifra.ifra_addr.sin6_addr)); |
3641 | in6_len2mask(&mask, pr->ndpr_plen); |
3642 | ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; |
3643 | ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; |
3644 | ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; |
3645 | ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; |
3646 | |
3647 | ndi = ND_IFINFO(ifp); |
3648 | VERIFY(ndi->initialized); |
3649 | lck_mtx_lock(&ndi->lock); |
3650 | |
3651 | notcga = nd6_send_opstate == ND6_SEND_OPMODE_DISABLED || |
3652 | (ndi->flags & ND6_IFF_INSECURE) != 0; |
3653 | |
3654 | lck_mtx_unlock(&ndi->lock); |
3655 | NDPR_UNLOCK(pr); |
3656 | |
3657 | if (notcga && !is_clat46) { |
3658 | ia6 = in6ifa_ifpforlinklocal(ifp, 0); |
3659 | if (ia6 == NULL) { |
3660 | error = EADDRNOTAVAIL; |
3661 | nd6log((LOG_INFO, "%s: no link-local address (%s)\n" , |
3662 | __func__, if_name(ifp))); |
3663 | goto done; |
3664 | } |
3665 | |
3666 | IFA_LOCK(&ia6->ia_ifa); |
3667 | ifra.ifra_addr.sin6_addr.s6_addr32[0] |= |
3668 | (ia6->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); |
3669 | ifra.ifra_addr.sin6_addr.s6_addr32[1] |= |
3670 | (ia6->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); |
3671 | ifra.ifra_addr.sin6_addr.s6_addr32[2] |= |
3672 | (ia6->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); |
3673 | ifra.ifra_addr.sin6_addr.s6_addr32[3] |= |
3674 | (ia6->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); |
3675 | IFA_UNLOCK(&ia6->ia_ifa); |
3676 | IFA_REMREF(&ia6->ia_ifa); |
3677 | ia6 = NULL; |
3678 | } else { |
3679 | in6_cga_node_lock(); |
3680 | struct in6_cga_prepare local_cga_prepare; |
3681 | |
3682 | /* |
3683 | * XXX For now the collision count is not used in the classical |
3684 | * way for secure addresses. |
3685 | * Use a different collision count value to generate reserved |
3686 | * address for stateless CLAT46 |
3687 | */ |
3688 | if (ndi->cga_initialized) { |
3689 | bcopy(&(ndi->local_cga_modifier), |
3690 | &(local_cga_prepare.cga_modifier), |
3691 | sizeof(local_cga_prepare.cga_modifier)); |
3692 | if (!is_clat46) { |
3693 | error = in6_cga_generate(&local_cga_prepare, 0, |
3694 | &ifra.ifra_addr.sin6_addr); |
3695 | } else { |
3696 | error = in6_cga_generate(&local_cga_prepare, 1, |
3697 | &ifra.ifra_addr.sin6_addr); |
3698 | } |
3699 | } else { |
3700 | if (!is_clat46) |
3701 | error = in6_cga_generate(NULL, 0, |
3702 | &ifra.ifra_addr.sin6_addr); |
3703 | else |
3704 | error = in6_cga_generate(NULL, 1, |
3705 | &ifra.ifra_addr.sin6_addr); |
3706 | } |
3707 | in6_cga_node_unlock(); |
3708 | if (error == 0) { |
3709 | ifra.ifra_flags |= IN6_IFF_SECURED; |
3710 | if (is_clat46) |
3711 | ifra.ifra_flags |= IN6_IFF_CLAT46; |
3712 | } else { |
3713 | if (!is_clat46) |
3714 | nd6log((LOG_ERR, "%s: no CGA available (%s)\n" , |
3715 | __func__, if_name(ifp))); |
3716 | else |
3717 | nd6log((LOG_ERR, "%s: no CLAT46 available (%s)\n" , |
3718 | __func__, if_name(ifp))); |
3719 | goto done; |
3720 | } |
3721 | } |
3722 | |
3723 | VERIFY(ia6 == NULL); |
3724 | |
3725 | /* new prefix mask. */ |
3726 | ifra.ifra_prefixmask.sin6_len = sizeof (struct sockaddr_in6); |
3727 | ifra.ifra_prefixmask.sin6_family = AF_INET6; |
3728 | bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, |
3729 | sizeof (ifra.ifra_prefixmask.sin6_addr)); |
3730 | |
3731 | /* lifetimes. */ |
3732 | ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; |
3733 | ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; |
3734 | |
3735 | /* address flags */ |
3736 | ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ |
3737 | |
3738 | /* |
3739 | * Make sure that we do not have this address already. This should |
3740 | * usually not happen, but we can still see this case, e.g., if we |
3741 | * have manually configured the exact address to be configured. |
3742 | */ |
3743 | if ((ia6 = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr)) |
3744 | != NULL) { |
3745 | error = EEXIST; |
3746 | IFA_REMREF(&ia6->ia_ifa); |
3747 | ia6 = NULL; |
3748 | |
3749 | /* this should be rare enough to make an explicit log */ |
3750 | log(LOG_INFO, "%s: %s is already configured!\n" , |
3751 | __func__, ip6_sprintf(&ifra.ifra_addr.sin6_addr)); |
3752 | goto done; |
3753 | } |
3754 | |
3755 | /* |
3756 | * Allocate ifaddr structure, link into chain, etc. |
3757 | * If we are going to create a new address upon receiving a multicasted |
3758 | * RA, we need to impose a random delay before starting DAD. |
3759 | * [RFC 4862, Section 5.4.2] |
3760 | */ |
3761 | ifaupdate = IN6_IFAUPDATE_NOWAIT; |
3762 | if (mcast) |
3763 | ifaupdate |= IN6_IFAUPDATE_DADDELAY; |
3764 | error = in6_update_ifa(ifp, &ifra, ifaupdate, &ia6); |
3765 | if (error != 0) { |
3766 | nd6log((LOG_ERR, |
3767 | "%s: failed to make ifaddr %s on %s (errno=%d)\n" , |
3768 | __func__, ip6_sprintf(&ifra.ifra_addr.sin6_addr), |
3769 | if_name(ifp), error)); |
3770 | error = EADDRNOTAVAIL; |
3771 | goto done; |
3772 | } |
3773 | |
3774 | VERIFY(ia6 != NULL); |
3775 | in6_post_msg(ifp, KEV_INET6_NEW_RTADV_ADDR, ia6, NULL); |
3776 | goto done; |
3777 | |
3778 | unlock1: |
3779 | NDPR_UNLOCK(pr); |
3780 | |
3781 | done: |
3782 | *errorp = error; |
3783 | return (ia6); |
3784 | } |
3785 | |
3786 | #define IA6_NONCONST(i) ((struct in6_ifaddr *)(uintptr_t)(i)) |
3787 | |
3788 | int |
3789 | in6_tmpifadd(const struct in6_ifaddr *ia0, int forcegen) |
3790 | { |
3791 | struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; |
3792 | struct in6_ifaddr *ia, *newia; |
3793 | struct in6_aliasreq ifra; |
3794 | int i, error, ifaupdate; |
3795 | int trylimit = 3; /* XXX: adhoc value */ |
3796 | u_int32_t randid[2]; |
3797 | time_t vltime0, pltime0; |
3798 | uint64_t timenow = net_uptime(); |
3799 | struct in6_addr addr; |
3800 | struct nd_prefix *ndpr; |
3801 | |
3802 | bzero(&ifra, sizeof (ifra)); |
3803 | strlcpy(ifra.ifra_name, if_name(ifp), sizeof (ifra.ifra_name)); |
3804 | IFA_LOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3805 | ifra.ifra_addr = ia0->ia_addr; |
3806 | /* copy prefix mask */ |
3807 | ifra.ifra_prefixmask = ia0->ia_prefixmask; |
3808 | /* clear the old IFID */ |
3809 | for (i = 0; i < 4; i++) { |
3810 | ifra.ifra_addr.sin6_addr.s6_addr32[i] |
3811 | &= ifra.ifra_prefixmask.sin6_addr.s6_addr32[i]; |
3812 | } |
3813 | addr = ia0->ia_addr.sin6_addr; |
3814 | IFA_UNLOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3815 | |
3816 | again: |
3817 | in6_iid_mktmp(ifp, (u_int8_t *)randid, |
3818 | (const u_int8_t *)&addr.s6_addr[8], forcegen); |
3819 | |
3820 | ifra.ifra_addr.sin6_addr.s6_addr32[2] |= |
3821 | (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2])); |
3822 | ifra.ifra_addr.sin6_addr.s6_addr32[3] |= |
3823 | (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3])); |
3824 | |
3825 | /* |
3826 | * in6_iid_mktmp() quite likely provided a unique interface ID. |
3827 | * However, we may still have a chance to see collision, because |
3828 | * there may be a time lag between generation of the ID and generation |
3829 | * of the address. So, we'll do one more sanity check. |
3830 | */ |
3831 | if ((ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr)) != NULL) { |
3832 | IFA_REMREF(&ia->ia_ifa); |
3833 | if (trylimit-- == 0) { |
3834 | nd6log((LOG_NOTICE, "in6_tmpifadd: failed to find " |
3835 | "a unique random IFID\n" )); |
3836 | return (EEXIST); |
3837 | } |
3838 | forcegen = 1; |
3839 | goto again; |
3840 | } |
3841 | |
3842 | /* |
3843 | * The Valid Lifetime is the lower of the Valid Lifetime of the |
3844 | * public address or TEMP_VALID_LIFETIME. |
3845 | * The Preferred Lifetime is the lower of the Preferred Lifetime |
3846 | * of the public address or TEMP_PREFERRED_LIFETIME - |
3847 | * DESYNC_FACTOR. |
3848 | */ |
3849 | IFA_LOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3850 | if (ia0->ia6_lifetime.ia6ti_vltime != ND6_INFINITE_LIFETIME) { |
3851 | vltime0 = IFA6_IS_INVALID(ia0, timenow) ? 0 : |
3852 | (ia0->ia6_lifetime.ia6ti_vltime - |
3853 | (timenow - ia0->ia6_updatetime)); |
3854 | if (vltime0 > ip6_temp_valid_lifetime) |
3855 | vltime0 = ip6_temp_valid_lifetime; |
3856 | } else { |
3857 | vltime0 = ip6_temp_valid_lifetime; |
3858 | } |
3859 | if (ia0->ia6_lifetime.ia6ti_pltime != ND6_INFINITE_LIFETIME) { |
3860 | pltime0 = IFA6_IS_DEPRECATED(ia0, timenow) ? 0 : |
3861 | (ia0->ia6_lifetime.ia6ti_pltime - |
3862 | (timenow - ia0->ia6_updatetime)); |
3863 | if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor) |
3864 | pltime0 = ip6_temp_preferred_lifetime - |
3865 | ip6_desync_factor; |
3866 | } else { |
3867 | pltime0 = ip6_temp_preferred_lifetime - ip6_desync_factor; |
3868 | } |
3869 | ifra.ifra_lifetime.ia6t_vltime = vltime0; |
3870 | ifra.ifra_lifetime.ia6t_pltime = pltime0; |
3871 | IFA_UNLOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3872 | /* |
3873 | * A temporary address is created only if this calculated Preferred |
3874 | * Lifetime is greater than REGEN_ADVANCE time units. |
3875 | */ |
3876 | if (ifra.ifra_lifetime.ia6t_pltime <= ip6_temp_regen_advance) |
3877 | return (0); |
3878 | |
3879 | /* XXX: scope zone ID? */ |
3880 | |
3881 | ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY); |
3882 | |
3883 | /* allocate ifaddr structure, link into chain, etc. */ |
3884 | ifaupdate = IN6_IFAUPDATE_NOWAIT | IN6_IFAUPDATE_DADDELAY; |
3885 | error = in6_update_ifa(ifp, &ifra, ifaupdate, &newia); |
3886 | if (error != 0) { |
3887 | nd6log((LOG_ERR, "in6_tmpifadd: failed to add address.\n" )); |
3888 | return (error); |
3889 | } |
3890 | VERIFY(newia != NULL); |
3891 | |
3892 | IFA_LOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3893 | ndpr = ia0->ia6_ndpr; |
3894 | if (ndpr == NULL) { |
3895 | /* |
3896 | * We lost the race with another thread that has purged |
3897 | * ia0 address; in this case, purge the tmp addr as well. |
3898 | */ |
3899 | nd6log((LOG_ERR, "in6_tmpifadd: no public address\n" )); |
3900 | VERIFY(!(ia0->ia6_flags & IN6_IFF_AUTOCONF)); |
3901 | IFA_UNLOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3902 | in6_purgeaddr(&newia->ia_ifa); |
3903 | IFA_REMREF(&newia->ia_ifa); |
3904 | return (EADDRNOTAVAIL); |
3905 | } |
3906 | NDPR_ADDREF(ndpr); /* for us */ |
3907 | IFA_UNLOCK(&IA6_NONCONST(ia0)->ia_ifa); |
3908 | IFA_LOCK(&newia->ia_ifa); |
3909 | if (newia->ia6_ndpr != NULL) { |
3910 | NDPR_LOCK(newia->ia6_ndpr); |
3911 | VERIFY(newia->ia6_ndpr->ndpr_addrcnt != 0); |
3912 | newia->ia6_ndpr->ndpr_addrcnt--; |
3913 | NDPR_UNLOCK(newia->ia6_ndpr); |
3914 | NDPR_REMREF(newia->ia6_ndpr); /* release addr reference */ |
3915 | } |
3916 | newia->ia6_ndpr = ndpr; |
3917 | NDPR_LOCK(newia->ia6_ndpr); |
3918 | newia->ia6_ndpr->ndpr_addrcnt++; |
3919 | VERIFY(newia->ia6_ndpr->ndpr_addrcnt != 0); |
3920 | NDPR_ADDREF_LOCKED(newia->ia6_ndpr); /* for addr reference */ |
3921 | NDPR_UNLOCK(newia->ia6_ndpr); |
3922 | IFA_UNLOCK(&newia->ia_ifa); |
3923 | /* |
3924 | * A newly added address might affect the status of other addresses. |
3925 | * XXX: when the temporary address is generated with a new public |
3926 | * address, the onlink check is redundant. However, it would be safe |
3927 | * to do the check explicitly everywhere a new address is generated, |
3928 | * and, in fact, we surely need the check when we create a new |
3929 | * temporary address due to deprecation of an old temporary address. |
3930 | */ |
3931 | lck_mtx_lock(nd6_mutex); |
3932 | pfxlist_onlink_check(); |
3933 | lck_mtx_unlock(nd6_mutex); |
3934 | IFA_REMREF(&newia->ia_ifa); |
3935 | |
3936 | /* remove our reference */ |
3937 | NDPR_REMREF(ndpr); |
3938 | |
3939 | return (0); |
3940 | } |
3941 | #undef IA6_NONCONST |
3942 | |
3943 | int |
3944 | in6_init_prefix_ltimes(struct nd_prefix *ndpr) |
3945 | { |
3946 | struct timeval caltime; |
3947 | u_int64_t timenow = net_uptime(); |
3948 | |
3949 | NDPR_LOCK_ASSERT_HELD(ndpr); |
3950 | |
3951 | getmicrotime(&caltime); |
3952 | ndpr->ndpr_base_calendartime = caltime.tv_sec; |
3953 | ndpr->ndpr_base_uptime = timenow; |
3954 | |
3955 | /* check if preferred lifetime > valid lifetime. RFC 4862 5.5.3 (c) */ |
3956 | if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { |
3957 | nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" |
3958 | "(%d) is greater than valid lifetime(%d)\n" , |
3959 | (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime)); |
3960 | return (EINVAL); |
3961 | } |
3962 | if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) |
3963 | ndpr->ndpr_preferred = 0; |
3964 | else |
3965 | ndpr->ndpr_preferred = timenow + ndpr->ndpr_pltime; |
3966 | if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) |
3967 | ndpr->ndpr_expire = 0; |
3968 | else |
3969 | ndpr->ndpr_expire = timenow + ndpr->ndpr_vltime; |
3970 | |
3971 | return (0); |
3972 | } |
3973 | |
3974 | static void |
3975 | in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) |
3976 | { |
3977 | #pragma unused(new) |
3978 | uint64_t timenow = net_uptime(); |
3979 | |
3980 | /* Valid lifetime must not be updated unless explicitly specified. */ |
3981 | /* init ia6t_expire */ |
3982 | if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) { |
3983 | lt6->ia6t_expire = 0; |
3984 | } else { |
3985 | lt6->ia6t_expire = timenow; |
3986 | lt6->ia6t_expire += lt6->ia6t_vltime; |
3987 | } |
3988 | |
3989 | /* init ia6t_preferred */ |
3990 | if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) { |
3991 | lt6->ia6t_preferred = 0; |
3992 | } else { |
3993 | lt6->ia6t_preferred = timenow; |
3994 | lt6->ia6t_preferred += lt6->ia6t_pltime; |
3995 | } |
3996 | } |
3997 | |
3998 | /* |
3999 | * Delete all the routing table entries that use the specified gateway. |
4000 | * XXX: this function causes search through all entries of routing table, so |
4001 | * it shouldn't be called when acting as a router. |
4002 | */ |
4003 | void |
4004 | rt6_flush( |
4005 | struct in6_addr *gateway, |
4006 | struct ifnet *ifp) |
4007 | { |
4008 | struct radix_node_head *rnh = rt_tables[AF_INET6]; |
4009 | |
4010 | /* We'll care only link-local addresses */ |
4011 | if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { |
4012 | return; |
4013 | } |
4014 | lck_mtx_lock(rnh_lock); |
4015 | /* XXX: hack for KAME's link-local address kludge */ |
4016 | gateway->s6_addr16[1] = htons(ifp->if_index); |
4017 | |
4018 | rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); |
4019 | lck_mtx_unlock(rnh_lock); |
4020 | } |
4021 | |
4022 | static int |
4023 | rt6_deleteroute( |
4024 | struct radix_node *rn, |
4025 | void *arg) |
4026 | { |
4027 | struct rtentry *rt = (struct rtentry *)rn; |
4028 | struct in6_addr *gate = (struct in6_addr *)arg; |
4029 | |
4030 | LCK_MTX_ASSERT(rnh_lock, LCK_MTX_ASSERT_OWNED); |
4031 | |
4032 | RT_LOCK(rt); |
4033 | if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) { |
4034 | RT_UNLOCK(rt); |
4035 | return (0); |
4036 | } |
4037 | |
4038 | if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr)) { |
4039 | RT_UNLOCK(rt); |
4040 | return (0); |
4041 | } |
4042 | /* |
4043 | * Do not delete a static route. |
4044 | * XXX: this seems to be a bit ad-hoc. Should we consider the |
4045 | * 'cloned' bit instead? |
4046 | */ |
4047 | if ((rt->rt_flags & RTF_STATIC) != 0) { |
4048 | RT_UNLOCK(rt); |
4049 | return (0); |
4050 | } |
4051 | /* |
4052 | * We delete only host route. This means, in particular, we don't |
4053 | * delete default route. |
4054 | */ |
4055 | if ((rt->rt_flags & RTF_HOST) == 0) { |
4056 | RT_UNLOCK(rt); |
4057 | return (0); |
4058 | } |
4059 | |
4060 | /* |
4061 | * Safe to drop rt_lock and use rt_key, rt_gateway, since holding |
4062 | * rnh_lock here prevents another thread from calling rt_setgate() |
4063 | * on this route. |
4064 | */ |
4065 | RT_UNLOCK(rt); |
4066 | return (rtrequest_locked(RTM_DELETE, rt_key(rt), rt->rt_gateway, |
4067 | rt_mask(rt), rt->rt_flags, 0)); |
4068 | } |
4069 | |
4070 | int |
4071 | nd6_setdefaultiface( |
4072 | int ifindex) |
4073 | { |
4074 | int error = 0; |
4075 | ifnet_t def_ifp = NULL; |
4076 | |
4077 | LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); |
4078 | |
4079 | ifnet_head_lock_shared(); |
4080 | if (ifindex < 0 || if_index < ifindex) { |
4081 | ifnet_head_done(); |
4082 | return (EINVAL); |
4083 | } |
4084 | def_ifp = ifindex2ifnet[ifindex]; |
4085 | ifnet_head_done(); |
4086 | |
4087 | lck_mtx_lock(nd6_mutex); |
4088 | if (nd6_defifindex != ifindex) { |
4089 | struct ifnet *odef_ifp = nd6_defifp; |
4090 | |
4091 | nd6_defifindex = ifindex; |
4092 | if (nd6_defifindex > 0) |
4093 | nd6_defifp = def_ifp; |
4094 | else |
4095 | nd6_defifp = NULL; |
4096 | |
4097 | if (nd6_defifp != NULL) |
4098 | nd6log((LOG_INFO, "%s: is now the default " |
4099 | "interface (was %s)\n" , if_name(nd6_defifp), |
4100 | odef_ifp != NULL ? if_name(odef_ifp) : "NONE" )); |
4101 | else |
4102 | nd6log((LOG_INFO, "No default interface set\n" )); |
4103 | |
4104 | /* |
4105 | * If the Default Router List is empty, install a route |
4106 | * to the specified interface as default or remove the default |
4107 | * route when the default interface becomes canceled. |
4108 | * The check for the queue is actually redundant, but |
4109 | * we do this here to avoid re-install the default route |
4110 | * if the list is NOT empty. |
4111 | */ |
4112 | if (odef_ifp != NULL) { |
4113 | defrouter_select(odef_ifp); |
4114 | } |
4115 | |
4116 | if (nd6_defifp != NULL) { |
4117 | defrouter_select(nd6_defifp); |
4118 | nd6_prefix_sync(nd6_defifp); |
4119 | } |
4120 | |
4121 | /* |
4122 | * Our current implementation assumes one-to-one mapping between |
4123 | * interfaces and links, so it would be natural to use the |
4124 | * default interface as the default link. |
4125 | */ |
4126 | scope6_setdefault(nd6_defifp); |
4127 | } |
4128 | lck_mtx_unlock(nd6_mutex); |
4129 | return (error); |
4130 | } |
4131 | |