]>
Commit | Line | Data |
---|---|---|
2d21ac55 | 1 | /* |
b0d623f7 | 2 | * Copyright (c) 2003-2008 Apple Inc. All rights reserved. |
2d21ac55 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
55e303ae | 29 | /* $FreeBSD: src/sys/netinet6/nd6_rtr.c,v 1.11 2002/04/19 04:46:23 suz Exp $ */ |
9bccf70c | 30 | /* $KAME: nd6_rtr.c,v 1.111 2001/04/27 01:37:15 jinmei Exp $ */ |
1c79356b A |
31 | |
32 | /* | |
33 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
34 | * All rights reserved. | |
35 | * | |
36 | * Redistribution and use in source and binary forms, with or without | |
37 | * modification, are permitted provided that the following conditions | |
38 | * are met: | |
39 | * 1. Redistributions of source code must retain the above copyright | |
40 | * notice, this list of conditions and the following disclaimer. | |
41 | * 2. Redistributions in binary form must reproduce the above copyright | |
42 | * notice, this list of conditions and the following disclaimer in the | |
43 | * documentation and/or other materials provided with the distribution. | |
44 | * 3. Neither the name of the project nor the names of its contributors | |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | */ | |
60 | ||
9bccf70c | 61 | |
1c79356b A |
62 | #include <sys/param.h> |
63 | #include <sys/systm.h> | |
64 | #include <sys/malloc.h> | |
65 | #include <sys/mbuf.h> | |
66 | #include <sys/socket.h> | |
67 | #include <sys/sockio.h> | |
68 | #include <sys/time.h> | |
69 | #include <sys/kernel.h> | |
70 | #include <sys/errno.h> | |
1c79356b | 71 | #include <sys/syslog.h> |
9bccf70c | 72 | #include <sys/queue.h> |
91447636 | 73 | #include <kern/lock.h> |
1c79356b A |
74 | |
75 | #include <net/if.h> | |
76 | #include <net/if_types.h> | |
77 | #include <net/if_dl.h> | |
78 | #include <net/route.h> | |
79 | #include <net/radix.h> | |
80 | ||
81 | #include <netinet/in.h> | |
82 | #include <netinet6/in6_var.h> | |
9bccf70c | 83 | #include <netinet6/in6_ifattach.h> |
1c79356b A |
84 | #include <netinet/ip6.h> |
85 | #include <netinet6/ip6_var.h> | |
86 | #include <netinet6/nd6.h> | |
87 | #include <netinet/icmp6.h> | |
9bccf70c | 88 | #include <netinet6/scope6_var.h> |
1c79356b A |
89 | |
90 | #include <net/net_osdep.h> | |
91 | ||
92 | #define SDL(s) ((struct sockaddr_dl *)s) | |
93 | ||
91447636 A |
94 | static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); |
95 | static struct in6_ifaddr *in6_ifadd(struct nd_prefix *, | |
96 | struct in6_addr *); | |
97 | static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *, | |
98 | struct nd_defrouter *); | |
99 | static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *); | |
100 | static void pfxrtr_del(struct nd_pfxrouter *); | |
101 | static struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *); | |
102 | static void defrouter_addifreq(struct ifnet *); | |
103 | static void nd6_rtmsg(int, struct rtentry *); | |
1c79356b | 104 | |
91447636 A |
105 | static void in6_init_address_ltimes(struct nd_prefix *ndpr, |
106 | struct in6_addrlifetime *lt6); | |
1c79356b | 107 | |
91447636 | 108 | static int rt6_deleteroute(struct radix_node *, void *); |
1c79356b A |
109 | |
110 | extern int nd6_recalc_reachtm_interval; | |
111 | ||
9bccf70c | 112 | static struct ifnet *nd6_defifp; |
1c79356b A |
113 | int nd6_defifindex; |
114 | ||
9bccf70c | 115 | int ip6_use_tempaddr = 0; |
1c79356b | 116 | |
9bccf70c A |
117 | int ip6_desync_factor; |
118 | u_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME; | |
119 | u_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME; | |
120 | /* | |
121 | * shorter lifetimes for debugging purposes. | |
122 | int ip6_temp_preferred_lifetime = 800; | |
123 | static int ip6_temp_valid_lifetime = 1800; | |
124 | */ | |
125 | int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE; | |
1c79356b | 126 | |
91447636 A |
127 | extern lck_mtx_t *nd6_mutex; |
128 | ||
1c79356b A |
129 | /* |
130 | * Receive Router Solicitation Message - just for routers. | |
131 | * Router solicitation/advertisement is mostly managed by userland program | |
132 | * (rtadvd) so here we have no function like nd6_ra_output(). | |
133 | * | |
134 | * Based on RFC 2461 | |
135 | */ | |
136 | void | |
91447636 A |
137 | nd6_rs_input( |
138 | struct mbuf *m, | |
139 | int off, | |
140 | int icmp6len) | |
1c79356b A |
141 | { |
142 | struct ifnet *ifp = m->m_pkthdr.rcvif; | |
143 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | |
144 | struct nd_router_solicit *nd_rs; | |
145 | struct in6_addr saddr6 = ip6->ip6_src; | |
146 | #if 0 | |
147 | struct in6_addr daddr6 = ip6->ip6_dst; | |
148 | #endif | |
149 | char *lladdr = NULL; | |
150 | int lladdrlen = 0; | |
151 | #if 0 | |
152 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL; | |
153 | struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; | |
154 | struct rtentry *rt = NULL; | |
155 | int is_newentry; | |
156 | #endif | |
157 | union nd_opts ndopts; | |
158 | ||
159 | /* If I'm not a router, ignore it. */ | |
55e303ae | 160 | if (ip6_accept_rtadv != 0 || (ifp->if_eflags & IFEF_ACCEPT_RTADVD) || ip6_forwarding != 1) |
1c79356b A |
161 | goto freeit; |
162 | ||
163 | /* Sanity checks */ | |
164 | if (ip6->ip6_hlim != 255) { | |
9bccf70c A |
165 | nd6log((LOG_ERR, |
166 | "nd6_rs_input: invalid hlim (%d) from %s to %s on %s\n", | |
167 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), | |
168 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); | |
169 | goto bad; | |
1c79356b A |
170 | } |
171 | ||
172 | /* | |
173 | * Don't update the neighbor cache, if src = ::. | |
174 | * This indicates that the src has no IP address assigned yet. | |
175 | */ | |
176 | if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) | |
177 | goto freeit; | |
178 | ||
179 | #ifndef PULLDOWN_TEST | |
91447636 | 180 | IP6_EXTHDR_CHECK(m, off, icmp6len, return); |
1c79356b A |
181 | nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off); |
182 | #else | |
183 | IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len); | |
184 | if (nd_rs == NULL) { | |
185 | icmp6stat.icp6s_tooshort++; | |
186 | return; | |
187 | } | |
188 | #endif | |
189 | ||
190 | icmp6len -= sizeof(*nd_rs); | |
191 | nd6_option_init(nd_rs + 1, icmp6len, &ndopts); | |
192 | if (nd6_options(&ndopts) < 0) { | |
9bccf70c A |
193 | nd6log((LOG_INFO, |
194 | "nd6_rs_input: invalid ND option, ignored\n")); | |
195 | /* nd6_options have incremented stats */ | |
1c79356b A |
196 | goto freeit; |
197 | } | |
198 | ||
199 | if (ndopts.nd_opts_src_lladdr) { | |
200 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); | |
201 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; | |
202 | } | |
203 | ||
204 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { | |
9bccf70c | 205 | nd6log((LOG_INFO, |
1c79356b A |
206 | "nd6_rs_input: lladdrlen mismatch for %s " |
207 | "(if %d, RS packet %d)\n", | |
9bccf70c A |
208 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); |
209 | goto bad; | |
1c79356b A |
210 | } |
211 | ||
212 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); | |
213 | ||
214 | freeit: | |
215 | m_freem(m); | |
9bccf70c A |
216 | return; |
217 | ||
218 | bad: | |
219 | icmp6stat.icp6s_badrs++; | |
220 | m_freem(m); | |
1c79356b A |
221 | } |
222 | ||
223 | /* | |
224 | * Receive Router Advertisement Message. | |
225 | * | |
226 | * Based on RFC 2461 | |
227 | * TODO: on-link bit on prefix information | |
228 | * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing | |
229 | */ | |
230 | void | |
91447636 A |
231 | nd6_ra_input( |
232 | struct mbuf *m, | |
233 | int off, | |
234 | int icmp6len) | |
1c79356b A |
235 | { |
236 | struct ifnet *ifp = m->m_pkthdr.rcvif; | |
b0d623f7 | 237 | struct nd_ifinfo *ndi = NULL; |
1c79356b A |
238 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
239 | struct nd_router_advert *nd_ra; | |
240 | struct in6_addr saddr6 = ip6->ip6_src; | |
241 | #if 0 | |
242 | struct in6_addr daddr6 = ip6->ip6_dst; | |
243 | int flags; /* = nd_ra->nd_ra_flags_reserved; */ | |
244 | int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0); | |
245 | int is_other = ((flags & ND_RA_FLAG_OTHER) != 0); | |
246 | #endif | |
247 | union nd_opts ndopts; | |
248 | struct nd_defrouter *dr; | |
91447636 A |
249 | struct timeval timenow; |
250 | ||
251 | getmicrotime(&timenow); | |
1c79356b | 252 | |
55e303ae | 253 | if (ip6_accept_rtadv == 0 && ((ifp->if_eflags & IFEF_ACCEPT_RTADVD) == 0)) |
1c79356b A |
254 | goto freeit; |
255 | ||
256 | if (ip6->ip6_hlim != 255) { | |
9bccf70c A |
257 | nd6log((LOG_ERR, |
258 | "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n", | |
259 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), | |
260 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); | |
261 | goto bad; | |
1c79356b A |
262 | } |
263 | ||
264 | if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { | |
9bccf70c | 265 | nd6log((LOG_ERR, |
1c79356b | 266 | "nd6_ra_input: src %s is not link-local\n", |
9bccf70c A |
267 | ip6_sprintf(&saddr6))); |
268 | goto bad; | |
1c79356b A |
269 | } |
270 | ||
271 | #ifndef PULLDOWN_TEST | |
91447636 | 272 | IP6_EXTHDR_CHECK(m, off, icmp6len, return); |
1c79356b A |
273 | nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); |
274 | #else | |
275 | IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); | |
276 | if (nd_ra == NULL) { | |
277 | icmp6stat.icp6s_tooshort++; | |
278 | return; | |
279 | } | |
280 | #endif | |
281 | ||
282 | icmp6len -= sizeof(*nd_ra); | |
283 | nd6_option_init(nd_ra + 1, icmp6len, &ndopts); | |
284 | if (nd6_options(&ndopts) < 0) { | |
9bccf70c A |
285 | nd6log((LOG_INFO, |
286 | "nd6_ra_input: invalid ND option, ignored\n")); | |
287 | /* nd6_options have incremented stats */ | |
1c79356b A |
288 | goto freeit; |
289 | } | |
290 | ||
291 | { | |
292 | struct nd_defrouter dr0; | |
293 | u_int32_t advreachable = nd_ra->nd_ra_reachable; | |
1c79356b | 294 | |
b0d623f7 A |
295 | lck_rw_lock_shared(nd_if_rwlock); |
296 | if (ifp->if_index >= nd_ifinfo_indexlim) { | |
297 | lck_rw_done(nd_if_rwlock); | |
298 | goto freeit; | |
299 | } | |
300 | ndi = &nd_ifinfo[ifp->if_index]; | |
1c79356b A |
301 | dr0.rtaddr = saddr6; |
302 | dr0.flags = nd_ra->nd_ra_flags_reserved; | |
303 | dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); | |
91447636 | 304 | dr0.expire = timenow.tv_sec + dr0.rtlifetime; |
1c79356b A |
305 | dr0.ifp = ifp; |
306 | dr0.advint = 0; /* Mobile IPv6 */ | |
307 | dr0.advint_expire = 0; /* Mobile IPv6 */ | |
308 | dr0.advints_lost = 0; /* Mobile IPv6 */ | |
309 | /* unspecified or not? (RFC 2461 6.3.4) */ | |
310 | if (advreachable) { | |
55e303ae | 311 | advreachable = ntohl(advreachable); |
1c79356b A |
312 | if (advreachable <= MAX_REACHABLE_TIME && |
313 | ndi->basereachable != advreachable) { | |
314 | ndi->basereachable = advreachable; | |
315 | ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); | |
316 | ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ | |
317 | } | |
318 | } | |
319 | if (nd_ra->nd_ra_retransmit) | |
320 | ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); | |
321 | if (nd_ra->nd_ra_curhoplimit) | |
322 | ndi->chlim = nd_ra->nd_ra_curhoplimit; | |
b0d623f7 A |
323 | lck_rw_done(nd_if_rwlock); |
324 | ndi = NULL; | |
1c79356b A |
325 | dr = defrtrlist_update(&dr0); |
326 | } | |
327 | ||
328 | /* | |
329 | * prefix | |
330 | */ | |
331 | if (ndopts.nd_opts_pi) { | |
332 | struct nd_opt_hdr *pt; | |
9bccf70c | 333 | struct nd_opt_prefix_info *pi = NULL; |
1c79356b A |
334 | struct nd_prefix pr; |
335 | ||
336 | for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; | |
337 | pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; | |
338 | pt = (struct nd_opt_hdr *)((caddr_t)pt + | |
339 | (pt->nd_opt_len << 3))) { | |
340 | if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) | |
341 | continue; | |
342 | pi = (struct nd_opt_prefix_info *)pt; | |
343 | ||
344 | if (pi->nd_opt_pi_len != 4) { | |
9bccf70c A |
345 | nd6log((LOG_INFO, |
346 | "nd6_ra_input: invalid option " | |
347 | "len %d for prefix information option, " | |
348 | "ignored\n", pi->nd_opt_pi_len)); | |
1c79356b A |
349 | continue; |
350 | } | |
351 | ||
352 | if (128 < pi->nd_opt_pi_prefix_len) { | |
9bccf70c A |
353 | nd6log((LOG_INFO, |
354 | "nd6_ra_input: invalid prefix " | |
355 | "len %d for prefix information option, " | |
356 | "ignored\n", pi->nd_opt_pi_prefix_len)); | |
1c79356b A |
357 | continue; |
358 | } | |
359 | ||
360 | if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) | |
361 | || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { | |
9bccf70c A |
362 | nd6log((LOG_INFO, |
363 | "nd6_ra_input: invalid prefix " | |
364 | "%s, ignored\n", | |
365 | ip6_sprintf(&pi->nd_opt_pi_prefix))); | |
1c79356b A |
366 | continue; |
367 | } | |
368 | ||
369 | /* aggregatable unicast address, rfc2374 */ | |
370 | if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 | |
371 | && pi->nd_opt_pi_prefix_len != 64) { | |
9bccf70c A |
372 | nd6log((LOG_INFO, |
373 | "nd6_ra_input: invalid prefixlen " | |
374 | "%d for rfc2374 prefix %s, ignored\n", | |
375 | pi->nd_opt_pi_prefix_len, | |
376 | ip6_sprintf(&pi->nd_opt_pi_prefix))); | |
1c79356b A |
377 | continue; |
378 | } | |
379 | ||
380 | bzero(&pr, sizeof(pr)); | |
381 | pr.ndpr_prefix.sin6_family = AF_INET6; | |
382 | pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); | |
383 | pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; | |
91447636 | 384 | pr.ndpr_ifp = m->m_pkthdr.rcvif; |
1c79356b A |
385 | |
386 | pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & | |
387 | ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; | |
388 | pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & | |
389 | ND_OPT_PI_FLAG_AUTO) ? 1 : 0; | |
390 | pr.ndpr_plen = pi->nd_opt_pi_prefix_len; | |
391 | pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); | |
392 | pr.ndpr_pltime = | |
393 | ntohl(pi->nd_opt_pi_preferred_time); | |
394 | ||
395 | if (in6_init_prefix_ltimes(&pr)) | |
396 | continue; /* prefix lifetime init failed */ | |
397 | ||
398 | (void)prelist_update(&pr, dr, m); | |
399 | } | |
400 | } | |
401 | ||
402 | /* | |
403 | * MTU | |
404 | */ | |
405 | if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { | |
406 | u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); | |
407 | ||
408 | /* lower bound */ | |
409 | if (mtu < IPV6_MMTU) { | |
9bccf70c | 410 | nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option " |
1c79356b | 411 | "mtu=%d sent from %s, ignoring\n", |
9bccf70c | 412 | mtu, ip6_sprintf(&ip6->ip6_src))); |
1c79356b A |
413 | goto skip; |
414 | } | |
415 | ||
b0d623f7 A |
416 | lck_rw_lock_shared(nd_if_rwlock); |
417 | if (ifp->if_index >= nd_ifinfo_indexlim) { | |
418 | lck_rw_done(nd_if_rwlock); | |
419 | goto freeit; | |
420 | } | |
421 | ndi = &nd_ifinfo[ifp->if_index]; | |
1c79356b A |
422 | /* upper bound */ |
423 | if (ndi->maxmtu) { | |
424 | if (mtu <= ndi->maxmtu) { | |
425 | int change = (ndi->linkmtu != mtu); | |
426 | ||
427 | ndi->linkmtu = mtu; | |
b0d623f7 | 428 | lck_rw_done(nd_if_rwlock); |
1c79356b A |
429 | if (change) /* in6_maxmtu may change */ |
430 | in6_setmaxmtu(); | |
431 | } else { | |
9bccf70c | 432 | nd6log((LOG_INFO, "nd6_ra_input: bogus mtu " |
1c79356b A |
433 | "mtu=%d sent from %s; " |
434 | "exceeds maxmtu %d, ignoring\n", | |
435 | mtu, ip6_sprintf(&ip6->ip6_src), | |
9bccf70c | 436 | ndi->maxmtu)); |
b0d623f7 | 437 | lck_rw_done(nd_if_rwlock); |
1c79356b A |
438 | } |
439 | } else { | |
b0d623f7 | 440 | lck_rw_done(nd_if_rwlock); |
9bccf70c | 441 | nd6log((LOG_INFO, "nd6_ra_input: mtu option " |
1c79356b A |
442 | "mtu=%d sent from %s; maxmtu unknown, " |
443 | "ignoring\n", | |
9bccf70c | 444 | mtu, ip6_sprintf(&ip6->ip6_src))); |
1c79356b | 445 | } |
b0d623f7 | 446 | ndi = NULL; |
1c79356b A |
447 | } |
448 | ||
449 | skip: | |
450 | ||
451 | /* | |
55e303ae | 452 | * Source link layer address |
1c79356b A |
453 | */ |
454 | { | |
455 | char *lladdr = NULL; | |
456 | int lladdrlen = 0; | |
457 | ||
458 | if (ndopts.nd_opts_src_lladdr) { | |
459 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); | |
460 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; | |
461 | } | |
462 | ||
463 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { | |
9bccf70c | 464 | nd6log((LOG_INFO, |
1c79356b A |
465 | "nd6_ra_input: lladdrlen mismatch for %s " |
466 | "(if %d, RA packet %d)\n", | |
9bccf70c A |
467 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2)); |
468 | goto bad; | |
1c79356b A |
469 | } |
470 | ||
471 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); | |
472 | ||
473 | /* | |
474 | * Installing a link-layer address might change the state of the | |
475 | * router's neighbor cache, which might also affect our on-link | |
476 | * detection of adveritsed prefixes. | |
477 | */ | |
91447636 | 478 | pfxlist_onlink_check(0); |
1c79356b A |
479 | } |
480 | ||
9bccf70c A |
481 | freeit: |
482 | m_freem(m); | |
483 | return; | |
1c79356b | 484 | |
9bccf70c A |
485 | bad: |
486 | icmp6stat.icp6s_badra++; | |
1c79356b A |
487 | m_freem(m); |
488 | } | |
489 | ||
490 | /* | |
491 | * default router list proccessing sub routines | |
492 | */ | |
9bccf70c A |
493 | |
494 | /* tell the change to user processes watching the routing socket. */ | |
495 | static void | |
496 | nd6_rtmsg(cmd, rt) | |
497 | int cmd; | |
498 | struct rtentry *rt; | |
499 | { | |
500 | struct rt_addrinfo info; | |
b0d623f7 | 501 | struct ifnet *ifp = rt->rt_ifp; |
9bccf70c | 502 | |
b0d623f7 | 503 | RT_LOCK_ASSERT_HELD(rt); |
91447636 | 504 | |
9bccf70c | 505 | bzero((caddr_t)&info, sizeof(info)); |
b0d623f7 A |
506 | /* Lock ifp for if_addrlist */ |
507 | ifnet_lock_shared(ifp); | |
9bccf70c A |
508 | info.rti_info[RTAX_DST] = rt_key(rt); |
509 | info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; | |
510 | info.rti_info[RTAX_NETMASK] = rt_mask(rt); | |
511 | info.rti_info[RTAX_IFP] = | |
b0d623f7 | 512 | TAILQ_FIRST(&ifp->if_addrlist)->ifa_addr; |
9bccf70c A |
513 | info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; |
514 | ||
515 | rt_missmsg(cmd, &info, rt->rt_flags, 0); | |
b0d623f7 | 516 | ifnet_lock_done(ifp); |
9bccf70c A |
517 | } |
518 | ||
1c79356b | 519 | void |
91447636 A |
520 | defrouter_addreq( |
521 | struct nd_defrouter *new) | |
1c79356b A |
522 | { |
523 | struct sockaddr_in6 def, mask, gate; | |
9bccf70c | 524 | struct rtentry *newrt = NULL; |
1c79356b A |
525 | |
526 | Bzero(&def, sizeof(def)); | |
527 | Bzero(&mask, sizeof(mask)); | |
528 | Bzero(&gate, sizeof(gate)); | |
529 | ||
530 | def.sin6_len = mask.sin6_len = gate.sin6_len | |
531 | = sizeof(struct sockaddr_in6); | |
532 | def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; | |
533 | gate.sin6_addr = new->rtaddr; | |
534 | ||
b0d623f7 A |
535 | (void) rtrequest(RTM_ADD, (struct sockaddr *)&def, |
536 | (struct sockaddr *)&gate, (struct sockaddr *)&mask, | |
537 | RTF_GATEWAY, &newrt); | |
9bccf70c | 538 | if (newrt) { |
b0d623f7 | 539 | RT_LOCK(newrt); |
9bccf70c | 540 | nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ |
b0d623f7 A |
541 | RT_REMREF_LOCKED(newrt); |
542 | RT_UNLOCK(newrt); | |
9bccf70c | 543 | } |
1c79356b A |
544 | return; |
545 | } | |
546 | ||
547 | /* Add a route to a given interface as default */ | |
548 | void | |
91447636 A |
549 | defrouter_addifreq( |
550 | struct ifnet *ifp) | |
1c79356b A |
551 | { |
552 | struct sockaddr_in6 def, mask; | |
91447636 | 553 | struct ifaddr *ifa = NULL; |
9bccf70c | 554 | struct rtentry *newrt = NULL; |
91447636 | 555 | int error; |
b0d623f7 | 556 | u_int32_t flags; |
1c79356b A |
557 | |
558 | bzero(&def, sizeof(def)); | |
559 | bzero(&mask, sizeof(mask)); | |
560 | ||
561 | def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6); | |
562 | def.sin6_family = mask.sin6_family = AF_INET6; | |
563 | ||
564 | /* | |
565 | * Search for an ifaddr beloging to the specified interface. | |
566 | * XXX: An IPv6 address are required to be assigned on the interface. | |
567 | */ | |
568 | if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) { | |
9bccf70c | 569 | nd6log((LOG_ERR, /* better error? */ |
1c79356b A |
570 | "defrouter_addifreq: failed to find an ifaddr " |
571 | "to install a route to interface %s\n", | |
9bccf70c | 572 | if_name(ifp))); |
1c79356b A |
573 | return; |
574 | } | |
575 | ||
576 | flags = ifa->ifa_flags; | |
b0d623f7 A |
577 | error = rtrequest(RTM_ADD, (struct sockaddr *)&def, ifa->ifa_addr, |
578 | (struct sockaddr *)&mask, flags, &newrt); | |
9bccf70c A |
579 | if (error != 0) { |
580 | nd6log((LOG_ERR, | |
1c79356b A |
581 | "defrouter_addifreq: failed to install a route to " |
582 | "interface %s (errno = %d)\n", | |
9bccf70c | 583 | if_name(ifp), error)); |
9bccf70c A |
584 | } else { |
585 | if (newrt) { | |
b0d623f7 | 586 | RT_LOCK(newrt); |
9bccf70c | 587 | nd6_rtmsg(RTM_ADD, newrt); |
b0d623f7 A |
588 | RT_REMREF_LOCKED(newrt); |
589 | RT_UNLOCK(newrt); | |
9bccf70c | 590 | } |
55e303ae | 591 | in6_post_msg(ifp, KEV_INET6_DEFROUTER, (struct in6_ifaddr *)ifa); |
1c79356b | 592 | } |
91447636 | 593 | ifafree(ifa); |
1c79356b A |
594 | } |
595 | ||
596 | struct nd_defrouter * | |
91447636 A |
597 | defrouter_lookup( |
598 | struct in6_addr *addr, | |
599 | struct ifnet *ifp) | |
1c79356b A |
600 | { |
601 | struct nd_defrouter *dr; | |
602 | ||
91447636 A |
603 | |
604 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); | |
605 | ||
1c79356b A |
606 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
607 | dr = TAILQ_NEXT(dr, dr_entry)) { | |
608 | if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) | |
609 | return(dr); | |
610 | } | |
611 | ||
612 | return(NULL); /* search failed */ | |
613 | } | |
614 | ||
615 | void | |
91447636 A |
616 | defrouter_delreq( |
617 | struct nd_defrouter *dr, | |
618 | int dofree) | |
1c79356b A |
619 | { |
620 | struct sockaddr_in6 def, mask, gate; | |
9bccf70c | 621 | struct rtentry *oldrt = NULL; |
1c79356b A |
622 | |
623 | Bzero(&def, sizeof(def)); | |
624 | Bzero(&mask, sizeof(mask)); | |
625 | Bzero(&gate, sizeof(gate)); | |
626 | ||
627 | def.sin6_len = mask.sin6_len = gate.sin6_len | |
628 | = sizeof(struct sockaddr_in6); | |
629 | def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; | |
630 | gate.sin6_addr = dr->rtaddr; | |
631 | ||
b0d623f7 A |
632 | (void) rtrequest(RTM_DELETE, (struct sockaddr *)&def, |
633 | (struct sockaddr *)&gate, (struct sockaddr *)&mask, | |
634 | RTF_GATEWAY, &oldrt); | |
9bccf70c | 635 | if (oldrt) { |
b0d623f7 | 636 | RT_LOCK(oldrt); |
9bccf70c | 637 | nd6_rtmsg(RTM_DELETE, oldrt); |
b0d623f7 A |
638 | RT_UNLOCK(oldrt); |
639 | rtfree(oldrt); | |
9bccf70c | 640 | } |
1c79356b A |
641 | |
642 | if (dofree) /* XXX: necessary? */ | |
9bccf70c | 643 | FREE(dr, M_IP6NDP); |
1c79356b A |
644 | } |
645 | ||
646 | void | |
91447636 A |
647 | defrtrlist_del( |
648 | struct nd_defrouter *dr, int nd6locked) | |
1c79356b A |
649 | { |
650 | struct nd_defrouter *deldr = NULL; | |
651 | struct nd_prefix *pr; | |
b0d623f7 | 652 | struct ifnet *ifp = dr->ifp; |
1c79356b A |
653 | |
654 | /* | |
655 | * Flush all the routing table entries that use the router | |
656 | * as a next hop. | |
657 | */ | |
b0d623f7 A |
658 | if (!ip6_forwarding && |
659 | (ip6_accept_rtadv || (ifp->if_eflags & IFEF_ACCEPT_RTADVD))) { | |
1c79356b | 660 | /* above is a good condition? */ |
b0d623f7 | 661 | rt6_flush(&dr->rtaddr, ifp); |
1c79356b A |
662 | } |
663 | ||
91447636 A |
664 | if (nd6locked == 0) |
665 | lck_mtx_lock(nd6_mutex); | |
1c79356b A |
666 | if (dr == TAILQ_FIRST(&nd_defrouter)) |
667 | deldr = dr; /* The router is primary. */ | |
668 | ||
669 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); | |
670 | ||
671 | /* | |
672 | * Also delete all the pointers to the router in each prefix lists. | |
673 | */ | |
674 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
675 | struct nd_pfxrouter *pfxrtr; | |
676 | if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) | |
677 | pfxrtr_del(pfxrtr); | |
678 | } | |
91447636 | 679 | pfxlist_onlink_check(1); |
1c79356b A |
680 | |
681 | /* | |
682 | * If the router is the primary one, choose a new one. | |
683 | * Note that defrouter_select() will remove the current gateway | |
684 | * from the routing table. | |
685 | */ | |
686 | if (deldr) | |
687 | defrouter_select(); | |
688 | ||
b0d623f7 A |
689 | lck_rw_lock_shared(nd_if_rwlock); |
690 | if (ifp->if_index < nd_ifinfo_indexlim) { | |
691 | struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; | |
692 | ndi->ndefrouters--; | |
693 | if (ndi->ndefrouters < 0) { | |
694 | log(LOG_WARNING, "defrtrlist_del: negative " | |
695 | "count on %s\n", if_name(ifp)); | |
696 | } | |
e2fac8b1 | 697 | } |
b0d623f7 | 698 | lck_rw_done(nd_if_rwlock); |
e2fac8b1 | 699 | |
91447636 A |
700 | if (nd6locked == 0) |
701 | lck_mtx_unlock(nd6_mutex); | |
702 | ||
9bccf70c | 703 | FREE(dr, M_IP6NDP); |
1c79356b A |
704 | } |
705 | ||
706 | /* | |
707 | * Default Router Selection according to Section 6.3.6 of RFC 2461: | |
708 | * 1) Routers that are reachable or probably reachable should be | |
709 | * preferred. | |
710 | * 2) When no routers on the list are known to be reachable or | |
711 | * probably reachable, routers SHOULD be selected in a round-robin | |
712 | * fashion. | |
713 | * 3) If the Default Router List is empty, assume that all | |
714 | * destinations are on-link. | |
715 | */ | |
716 | void | |
717 | defrouter_select() | |
718 | { | |
1c79356b A |
719 | struct nd_defrouter *dr, anydr; |
720 | struct rtentry *rt = NULL; | |
721 | struct llinfo_nd6 *ln = NULL; | |
722 | ||
1c79356b A |
723 | /* |
724 | * Search for a (probably) reachable router from the list. | |
725 | */ | |
91447636 A |
726 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
727 | ||
1c79356b A |
728 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
729 | dr = TAILQ_NEXT(dr, dr_entry)) { | |
b0d623f7 A |
730 | /* Callee returns a locked route upon success */ |
731 | if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp, 0)) != NULL) { | |
732 | RT_LOCK_ASSERT_HELD(rt); | |
733 | if ((ln = rt->rt_llinfo) != NULL && | |
734 | ND6_IS_LLINFO_PROBREACH(ln)) { | |
735 | RT_REMREF_LOCKED(rt); | |
736 | RT_UNLOCK(rt); | |
737 | /* Got it, and move it to the head */ | |
738 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); | |
739 | TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry); | |
740 | break; | |
741 | } | |
742 | RT_REMREF_LOCKED(rt); | |
743 | RT_UNLOCK(rt); | |
1c79356b A |
744 | } |
745 | } | |
746 | ||
747 | if ((dr = TAILQ_FIRST(&nd_defrouter))) { | |
748 | /* | |
749 | * De-install the previous default gateway and install | |
750 | * a new one. | |
751 | * Note that if there is no reachable router in the list, | |
752 | * the head entry will be used anyway. | |
753 | * XXX: do we have to check the current routing table entry? | |
754 | */ | |
755 | bzero(&anydr, sizeof(anydr)); | |
756 | defrouter_delreq(&anydr, 0); | |
757 | defrouter_addreq(dr); | |
758 | } | |
759 | else { | |
760 | /* | |
761 | * The Default Router List is empty, so install the default | |
762 | * route to an inteface. | |
763 | * XXX: The specification does not say this mechanism should | |
764 | * be restricted to hosts, but this would be not useful | |
765 | * (even harmful) for routers. | |
766 | */ | |
767 | if (!ip6_forwarding) { | |
768 | /* | |
769 | * De-install the current default route | |
770 | * in advance. | |
771 | */ | |
772 | bzero(&anydr, sizeof(anydr)); | |
773 | defrouter_delreq(&anydr, 0); | |
774 | if (nd6_defifp) { | |
775 | /* | |
776 | * Install a route to the default interface | |
777 | * as default route. | |
9bccf70c A |
778 | * XXX: we enable this for host only, because |
779 | * this may override a default route installed | |
780 | * a user process (e.g. routing daemon) in a | |
781 | * router case. | |
1c79356b A |
782 | */ |
783 | defrouter_addifreq(nd6_defifp); | |
9bccf70c A |
784 | } else { |
785 | nd6log((LOG_INFO, "defrouter_select: " | |
1c79356b | 786 | "there's no default router and no default" |
9bccf70c A |
787 | " interface\n")); |
788 | } | |
1c79356b A |
789 | } |
790 | } | |
791 | ||
1c79356b A |
792 | return; |
793 | } | |
794 | ||
795 | static struct nd_defrouter * | |
91447636 A |
796 | defrtrlist_update( |
797 | struct nd_defrouter *new) | |
1c79356b A |
798 | { |
799 | struct nd_defrouter *dr, *n; | |
b0d623f7 A |
800 | struct ifnet *ifp = new->ifp; |
801 | struct nd_ifinfo *ndi; | |
1c79356b | 802 | |
91447636 | 803 | lck_mtx_lock(nd6_mutex); |
b0d623f7 | 804 | if ((dr = defrouter_lookup(&new->rtaddr, ifp)) != NULL) { |
1c79356b A |
805 | /* entry exists */ |
806 | if (new->rtlifetime == 0) { | |
91447636 | 807 | defrtrlist_del(dr, 1); |
1c79356b A |
808 | dr = NULL; |
809 | } else { | |
810 | /* override */ | |
811 | dr->flags = new->flags; /* xxx flag check */ | |
812 | dr->rtlifetime = new->rtlifetime; | |
813 | dr->expire = new->expire; | |
814 | } | |
91447636 | 815 | lck_mtx_unlock(nd6_mutex); |
1c79356b A |
816 | return(dr); |
817 | } | |
818 | ||
819 | /* entry does not exist */ | |
820 | if (new->rtlifetime == 0) { | |
91447636 | 821 | lck_mtx_unlock(nd6_mutex); |
1c79356b A |
822 | return(NULL); |
823 | } | |
824 | ||
b0d623f7 A |
825 | n = (struct nd_defrouter *)_MALLOC(sizeof(*n), M_IP6NDP, M_NOWAIT); |
826 | if (n == NULL) { | |
827 | lck_mtx_unlock(nd6_mutex); | |
828 | return(NULL); | |
829 | } | |
830 | ||
831 | lck_rw_lock_shared(nd_if_rwlock); | |
832 | if (ifp->if_index >= nd_ifinfo_indexlim) | |
833 | goto freeit; | |
834 | ndi = &nd_ifinfo[ifp->if_index]; | |
e2fac8b1 A |
835 | if (ip6_maxifdefrouters >= 0 && |
836 | ndi->ndefrouters >= ip6_maxifdefrouters) { | |
b0d623f7 A |
837 | freeit: |
838 | lck_rw_done(nd_if_rwlock); | |
e2fac8b1 | 839 | lck_mtx_unlock(nd6_mutex); |
b0d623f7 | 840 | FREE(n, M_IP6NDP); |
e2fac8b1 A |
841 | return (NULL); |
842 | } | |
b0d623f7 A |
843 | ndi->ndefrouters++; |
844 | lck_rw_done(nd_if_rwlock); | |
e2fac8b1 | 845 | |
1c79356b A |
846 | bzero(n, sizeof(*n)); |
847 | *n = *new; | |
848 | ||
849 | /* | |
850 | * Insert the new router at the end of the Default Router List. | |
851 | * If there is no other router, install it anyway. Otherwise, | |
852 | * just continue to use the current default router. | |
853 | */ | |
854 | TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); | |
855 | if (TAILQ_FIRST(&nd_defrouter) == n) | |
856 | defrouter_select(); | |
b0d623f7 | 857 | |
91447636 | 858 | lck_mtx_unlock(nd6_mutex); |
1c79356b A |
859 | return(n); |
860 | } | |
861 | ||
9bccf70c | 862 | static struct nd_pfxrouter * |
91447636 A |
863 | pfxrtr_lookup( |
864 | struct nd_prefix *pr, | |
865 | struct nd_defrouter *dr) | |
1c79356b A |
866 | { |
867 | struct nd_pfxrouter *search; | |
868 | ||
91447636 | 869 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1c79356b A |
870 | for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) { |
871 | if (search->router == dr) | |
872 | break; | |
873 | } | |
874 | ||
875 | return(search); | |
876 | } | |
877 | ||
878 | static void | |
91447636 A |
879 | pfxrtr_add( |
880 | struct nd_prefix *pr, | |
881 | struct nd_defrouter *dr) | |
1c79356b A |
882 | { |
883 | struct nd_pfxrouter *new; | |
884 | ||
91447636 A |
885 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
886 | ||
1c79356b A |
887 | new = (struct nd_pfxrouter *)_MALLOC(sizeof(*new), M_IP6NDP, M_NOWAIT); |
888 | if (new == NULL) | |
889 | return; | |
890 | bzero(new, sizeof(*new)); | |
891 | new->router = dr; | |
892 | ||
893 | LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); | |
894 | ||
91447636 | 895 | pfxlist_onlink_check(1); |
1c79356b A |
896 | } |
897 | ||
898 | static void | |
91447636 A |
899 | pfxrtr_del( |
900 | struct nd_pfxrouter *pfr) | |
1c79356b | 901 | { |
91447636 | 902 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1c79356b | 903 | LIST_REMOVE(pfr, pfr_entry); |
9bccf70c | 904 | FREE(pfr, M_IP6NDP); |
1c79356b A |
905 | } |
906 | ||
907 | struct nd_prefix * | |
91447636 A |
908 | nd6_prefix_lookup( |
909 | struct nd_prefix *pr) | |
1c79356b A |
910 | { |
911 | struct nd_prefix *search; | |
912 | ||
91447636 | 913 | lck_mtx_lock(nd6_mutex); |
1c79356b A |
914 | for (search = nd_prefix.lh_first; search; search = search->ndpr_next) { |
915 | if (pr->ndpr_ifp == search->ndpr_ifp && | |
916 | pr->ndpr_plen == search->ndpr_plen && | |
917 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, | |
918 | &search->ndpr_prefix.sin6_addr, | |
919 | pr->ndpr_plen) | |
920 | ) { | |
921 | break; | |
922 | } | |
923 | } | |
2d21ac55 A |
924 | if (search != NULL) |
925 | ndpr_hold(search, TRUE); | |
91447636 | 926 | lck_mtx_unlock(nd6_mutex); |
1c79356b A |
927 | |
928 | return(search); | |
929 | } | |
930 | ||
2d21ac55 A |
931 | void |
932 | ndpr_hold(struct nd_prefix *pr, boolean_t locked) | |
933 | { | |
934 | if (!locked) | |
935 | lck_mtx_lock(nd6_mutex); | |
936 | ||
937 | if (pr->ndpr_usecnt < 0) | |
938 | panic("%s: bad usecnt %d for pr %p\n", __func__, | |
939 | pr->ndpr_usecnt, pr); | |
940 | ||
941 | pr->ndpr_usecnt++; | |
942 | ||
943 | if (!locked) | |
944 | lck_mtx_unlock(nd6_mutex); | |
945 | } | |
946 | ||
947 | void | |
948 | ndpr_rele(struct nd_prefix *pr, boolean_t locked) | |
949 | { | |
950 | if (!locked) | |
951 | lck_mtx_lock(nd6_mutex); | |
952 | ||
953 | if (pr->ndpr_usecnt <= 0) | |
954 | panic("%s: bad usecnt %d for pr %p\n", __func__, | |
955 | pr->ndpr_usecnt, pr); | |
956 | ||
957 | pr->ndpr_usecnt--; | |
958 | ||
959 | if (!locked) | |
960 | lck_mtx_unlock(nd6_mutex); | |
961 | } | |
962 | ||
e2fac8b1 A |
963 | static void |
964 | purge_detached(struct ifnet *ifp) | |
965 | { | |
966 | struct nd_prefix *pr, *pr_next; | |
967 | struct in6_ifaddr *ia; | |
968 | struct ifaddr *ifa, *ifa_next; | |
b0d623f7 | 969 | |
e2fac8b1 A |
970 | lck_mtx_lock(nd6_mutex); |
971 | ||
972 | for (pr = nd_prefix.lh_first; pr; pr = pr_next) { | |
973 | pr_next = pr->ndpr_next; | |
974 | if (pr->ndpr_ifp != ifp || | |
975 | IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) || | |
976 | ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && | |
977 | !LIST_EMPTY(&pr->ndpr_advrtrs))) | |
978 | continue; | |
b0d623f7 A |
979 | repeat: |
980 | ifnet_lock_shared(ifp); | |
e2fac8b1 A |
981 | for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa_next) { |
982 | ifa_next = ifa->ifa_list.tqe_next; | |
983 | if (ifa->ifa_addr->sa_family != AF_INET6) | |
984 | continue; | |
985 | ia = (struct in6_ifaddr *)ifa; | |
986 | if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == | |
987 | IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) { | |
b0d623f7 A |
988 | ifaref(ifa); |
989 | /* | |
990 | * Purging the address requires writer access | |
991 | * to the address list, so drop the ifnet lock | |
992 | * now and repeat from beginning. | |
993 | */ | |
994 | ifnet_lock_done(ifp); | |
e2fac8b1 | 995 | in6_purgeaddr(ifa, 1); |
b0d623f7 A |
996 | ifafree(ifa); |
997 | goto repeat; | |
e2fac8b1 A |
998 | } |
999 | } | |
b0d623f7 | 1000 | ifnet_lock_done(ifp); |
e2fac8b1 A |
1001 | if (pr->ndpr_refcnt == 0) |
1002 | prelist_remove(pr, 1); | |
1003 | } | |
1004 | ||
1005 | lck_mtx_unlock(nd6_mutex); | |
1006 | } | |
1007 | ||
9bccf70c | 1008 | int |
91447636 A |
1009 | nd6_prelist_add( |
1010 | struct nd_prefix *pr, | |
1011 | struct nd_defrouter *dr, | |
1012 | struct nd_prefix **newp) | |
1c79356b | 1013 | { |
9bccf70c | 1014 | struct nd_prefix *new = NULL; |
b0d623f7 A |
1015 | struct ifnet *ifp = pr->ndpr_ifp; |
1016 | struct nd_ifinfo *ndi = NULL; | |
91447636 | 1017 | int i; |
e2fac8b1 A |
1018 | |
1019 | if (ip6_maxifprefixes >= 0) { | |
b0d623f7 A |
1020 | lck_rw_lock_shared(nd_if_rwlock); |
1021 | if (ifp->if_index >= nd_ifinfo_indexlim) { | |
1022 | lck_rw_done(nd_if_rwlock); | |
1023 | return (EINVAL); | |
1024 | } | |
1025 | ndi = &nd_ifinfo[ifp->if_index]; | |
1026 | if (ndi->nprefixes >= ip6_maxifprefixes / 2) { | |
1027 | lck_rw_done(nd_if_rwlock); | |
1028 | purge_detached(ifp); | |
1029 | lck_rw_lock_shared(nd_if_rwlock); | |
1030 | /* | |
1031 | * Refresh pointer since nd_ifinfo[] may have grown; | |
1032 | * repeating the bounds check against nd_ifinfo_indexlim | |
1033 | * isn't necessary since the array never shrinks. | |
1034 | */ | |
1035 | ndi = &nd_ifinfo[ifp->if_index]; | |
1036 | } | |
1037 | if (ndi->nprefixes >= ip6_maxifprefixes) { | |
1038 | lck_rw_done(nd_if_rwlock); | |
e2fac8b1 | 1039 | return(ENOMEM); |
b0d623f7 A |
1040 | } |
1041 | lck_rw_done(nd_if_rwlock); | |
e2fac8b1 | 1042 | } |
1c79356b A |
1043 | |
1044 | new = (struct nd_prefix *)_MALLOC(sizeof(*new), M_IP6NDP, M_NOWAIT); | |
1045 | if (new == NULL) | |
1046 | return ENOMEM; | |
1047 | bzero(new, sizeof(*new)); | |
1048 | *new = *pr; | |
9bccf70c A |
1049 | if (newp != NULL) |
1050 | *newp = new; | |
1c79356b A |
1051 | |
1052 | /* initilization */ | |
1c79356b A |
1053 | LIST_INIT(&new->ndpr_advrtrs); |
1054 | in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); | |
1055 | /* make prefix in the canonical form */ | |
1056 | for (i = 0; i < 4; i++) | |
1057 | new->ndpr_prefix.sin6_addr.s6_addr32[i] &= | |
1058 | new->ndpr_mask.s6_addr32[i]; | |
1059 | ||
1c79356b | 1060 | /* link ndpr_entry to nd_prefix list */ |
91447636 | 1061 | lck_mtx_lock(nd6_mutex); |
1c79356b | 1062 | LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); |
1c79356b | 1063 | |
2d21ac55 A |
1064 | new->ndpr_usecnt = 0; |
1065 | ndpr_hold(new, TRUE); | |
1066 | ||
9bccf70c A |
1067 | /* ND_OPT_PI_FLAG_ONLINK processing */ |
1068 | if (new->ndpr_raf_onlink) { | |
1069 | int e; | |
1070 | ||
91447636 | 1071 | if ((e = nd6_prefix_onlink(new, 0, 1)) != 0) { |
9bccf70c A |
1072 | nd6log((LOG_ERR, "nd6_prelist_add: failed to make " |
1073 | "the prefix %s/%d on-link on %s (errno=%d)\n", | |
1074 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
b0d623f7 | 1075 | pr->ndpr_plen, if_name(ifp), e)); |
9bccf70c A |
1076 | /* proceed anyway. XXX: is it correct? */ |
1077 | } | |
1078 | } | |
1079 | ||
1c79356b A |
1080 | if (dr) { |
1081 | pfxrtr_add(new, dr); | |
1c79356b | 1082 | } |
e2fac8b1 | 1083 | |
b0d623f7 A |
1084 | lck_rw_lock_shared(nd_if_rwlock); |
1085 | /* | |
1086 | * Refresh pointer since nd_ifinfo[] may have grown; | |
1087 | * repeating the bounds check against nd_ifinfo_indexlim | |
1088 | * isn't necessary since the array never shrinks. | |
1089 | */ | |
1090 | ndi = &nd_ifinfo[ifp->if_index]; | |
e2fac8b1 | 1091 | ndi->nprefixes++; |
b0d623f7 | 1092 | lck_rw_done(nd_if_rwlock); |
e2fac8b1 | 1093 | |
91447636 | 1094 | lck_mtx_unlock(nd6_mutex); |
1c79356b A |
1095 | |
1096 | return 0; | |
1097 | } | |
1098 | ||
1099 | void | |
91447636 A |
1100 | prelist_remove( |
1101 | struct nd_prefix *pr, int nd6locked) | |
1c79356b A |
1102 | { |
1103 | struct nd_pfxrouter *pfr, *next; | |
b0d623f7 | 1104 | struct ifnet *ifp = pr->ndpr_ifp; |
91447636 | 1105 | int e; |
1c79356b | 1106 | |
9bccf70c A |
1107 | /* make sure to invalidate the prefix until it is really freed. */ |
1108 | pr->ndpr_vltime = 0; | |
1109 | pr->ndpr_pltime = 0; | |
1110 | #if 0 | |
1111 | /* | |
1112 | * Though these flags are now meaningless, we'd rather keep the value | |
1113 | * not to confuse users when executing "ndp -p". | |
1114 | */ | |
1115 | pr->ndpr_raf_onlink = 0; | |
1116 | pr->ndpr_raf_auto = 0; | |
1c79356b | 1117 | #endif |
9bccf70c A |
1118 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 && |
1119 | (e = nd6_prefix_offlink(pr)) != 0) { | |
1120 | nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink " | |
1121 | "on %s, errno=%d\n", | |
1122 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
b0d623f7 | 1123 | pr->ndpr_plen, if_name(ifp), e)); |
9bccf70c A |
1124 | /* what should we do? */ |
1125 | } | |
1126 | ||
91447636 A |
1127 | if (nd6locked == 0) |
1128 | lck_mtx_lock(nd6_mutex); | |
2d21ac55 A |
1129 | |
1130 | if (pr->ndpr_usecnt > 0 || pr->ndpr_refcnt > 0) | |
1131 | goto done; /* notice here? */ | |
1132 | ||
1c79356b A |
1133 | /* unlink ndpr_entry from nd_prefix list */ |
1134 | LIST_REMOVE(pr, ndpr_entry); | |
1c79356b A |
1135 | |
1136 | /* free list of routers that adversed the prefix */ | |
1137 | for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { | |
1138 | next = pfr->pfr_next; | |
1139 | ||
9bccf70c | 1140 | FREE(pfr, M_IP6NDP); |
1c79356b | 1141 | } |
9bccf70c | 1142 | |
b0d623f7 A |
1143 | lck_rw_lock_shared(nd_if_rwlock); |
1144 | if (ifp->if_index < nd_ifinfo_indexlim) { | |
1145 | struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; | |
1146 | ndi->nprefixes--; | |
1147 | if (ndi->nprefixes < 0) { | |
1148 | log(LOG_WARNING, "prelist_remove: negative " | |
1149 | "count on %s\n", if_name(ifp)); | |
1150 | } | |
e2fac8b1 | 1151 | } |
b0d623f7 | 1152 | lck_rw_done(nd_if_rwlock); |
e2fac8b1 | 1153 | |
9bccf70c | 1154 | FREE(pr, M_IP6NDP); |
1c79356b | 1155 | |
91447636 | 1156 | pfxlist_onlink_check(1); |
2d21ac55 | 1157 | done: |
91447636 A |
1158 | if (nd6locked == 0) |
1159 | lck_mtx_unlock(nd6_mutex); | |
1c79356b A |
1160 | } |
1161 | ||
1c79356b | 1162 | int |
91447636 A |
1163 | prelist_update( |
1164 | struct nd_prefix *new, | |
1165 | struct nd_defrouter *dr, /* may be NULL */ | |
1166 | struct mbuf *m) | |
1c79356b | 1167 | { |
9bccf70c A |
1168 | struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; |
1169 | struct ifaddr *ifa; | |
1170 | struct ifnet *ifp = new->ndpr_ifp; | |
1c79356b | 1171 | struct nd_prefix *pr; |
1c79356b | 1172 | int error = 0; |
9bccf70c | 1173 | int newprefix = 0; |
1c79356b | 1174 | int auth; |
9bccf70c | 1175 | struct in6_addrlifetime lt6_tmp; |
91447636 | 1176 | struct timeval timenow; |
1c79356b A |
1177 | |
1178 | auth = 0; | |
1179 | if (m) { | |
1180 | /* | |
1181 | * Authenticity for NA consists authentication for | |
1182 | * both IP header and IP datagrams, doesn't it ? | |
1183 | */ | |
1184 | #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) | |
1185 | auth = (m->m_flags & M_AUTHIPHDR | |
1186 | && m->m_flags & M_AUTHIPDGM) ? 1 : 0; | |
1187 | #endif | |
1188 | } | |
1189 | ||
9bccf70c A |
1190 | |
1191 | if ((pr = nd6_prefix_lookup(new)) != NULL) { | |
1192 | /* | |
1193 | * nd6_prefix_lookup() ensures that pr and new have the same | |
1194 | * prefix on a same interface. | |
1195 | */ | |
1196 | ||
1197 | /* | |
1198 | * Update prefix information. Note that the on-link (L) bit | |
1199 | * and the autonomous (A) bit should NOT be changed from 1 | |
1200 | * to 0. | |
1201 | */ | |
1202 | if (new->ndpr_raf_onlink == 1) | |
1203 | pr->ndpr_raf_onlink = 1; | |
1204 | if (new->ndpr_raf_auto == 1) | |
1205 | pr->ndpr_raf_auto = 1; | |
1206 | if (new->ndpr_raf_onlink) { | |
1207 | pr->ndpr_vltime = new->ndpr_vltime; | |
1208 | pr->ndpr_pltime = new->ndpr_pltime; | |
1209 | pr->ndpr_preferred = new->ndpr_preferred; | |
1210 | pr->ndpr_expire = new->ndpr_expire; | |
1c79356b A |
1211 | } |
1212 | ||
9bccf70c A |
1213 | if (new->ndpr_raf_onlink && |
1214 | (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { | |
1215 | int e; | |
1216 | ||
91447636 | 1217 | if ((e = nd6_prefix_onlink(pr, 0, 0)) != 0) { |
9bccf70c A |
1218 | nd6log((LOG_ERR, |
1219 | "prelist_update: failed to make " | |
1220 | "the prefix %s/%d on-link on %s " | |
1221 | "(errno=%d)\n", | |
1222 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
1223 | pr->ndpr_plen, if_name(pr->ndpr_ifp), e)); | |
1224 | /* proceed anyway. XXX: is it correct? */ | |
1225 | } | |
1c79356b | 1226 | } |
91447636 A |
1227 | |
1228 | lck_mtx_lock(nd6_mutex); | |
9bccf70c A |
1229 | if (dr && pfxrtr_lookup(pr, dr) == NULL) |
1230 | pfxrtr_add(pr, dr); | |
91447636 | 1231 | lck_mtx_unlock(nd6_mutex); |
9bccf70c A |
1232 | } else { |
1233 | struct nd_prefix *newpr = NULL; | |
1234 | ||
1235 | newprefix = 1; | |
1236 | ||
1237 | if (new->ndpr_vltime == 0) | |
1238 | goto end; | |
1239 | if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) | |
1240 | goto end; | |
1241 | ||
1242 | bzero(&new->ndpr_addr, sizeof(struct in6_addr)); | |
1243 | ||
1244 | error = nd6_prelist_add(new, dr, &newpr); | |
1245 | if (error != 0 || newpr == NULL) { | |
1246 | nd6log((LOG_NOTICE, "prelist_update: " | |
1247 | "nd6_prelist_add failed for %s/%d on %s " | |
1248 | "errno=%d, returnpr=%p\n", | |
1249 | ip6_sprintf(&new->ndpr_prefix.sin6_addr), | |
1250 | new->ndpr_plen, if_name(new->ndpr_ifp), | |
1251 | error, newpr)); | |
1252 | goto end; /* we should just give up in this case. */ | |
1253 | } | |
1c79356b A |
1254 | |
1255 | /* | |
9bccf70c A |
1256 | * XXX: from the ND point of view, we can ignore a prefix |
1257 | * with the on-link bit being zero. However, we need a | |
1258 | * prefix structure for references from autoconfigured | |
1259 | * addresses. Thus, we explicitly make suret that the prefix | |
1260 | * itself expires now. | |
1c79356b | 1261 | */ |
9bccf70c A |
1262 | if (newpr->ndpr_raf_onlink == 0) { |
1263 | newpr->ndpr_vltime = 0; | |
1264 | newpr->ndpr_pltime = 0; | |
1265 | in6_init_prefix_ltimes(newpr); | |
1266 | } | |
1c79356b | 1267 | |
9bccf70c A |
1268 | pr = newpr; |
1269 | } | |
1c79356b | 1270 | |
9bccf70c A |
1271 | /* |
1272 | * Address autoconfiguration based on Section 5.5.3 of RFC 2462. | |
1273 | * Note that pr must be non NULL at this point. | |
1274 | */ | |
1c79356b | 1275 | |
9bccf70c A |
1276 | /* 5.5.3 (a). Ignore the prefix without the A bit set. */ |
1277 | if (!new->ndpr_raf_auto) | |
1278 | goto afteraddrconf; | |
1c79356b | 1279 | |
9bccf70c A |
1280 | /* |
1281 | * 5.5.3 (b). the link-local prefix should have been ignored in | |
1282 | * nd6_ra_input. | |
1283 | */ | |
1c79356b | 1284 | |
9bccf70c A |
1285 | /* |
1286 | * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. | |
1287 | * This should have been done in nd6_ra_input. | |
1288 | */ | |
1c79356b | 1289 | |
9bccf70c A |
1290 | /* |
1291 | * 5.5.3 (d). If the prefix advertised does not match the prefix of an | |
1292 | * address already in the list, and the Valid Lifetime is not 0, | |
1293 | * form an address. Note that even a manually configured address | |
1294 | * should reject autoconfiguration of a new address. | |
1295 | */ | |
91447636 A |
1296 | getmicrotime(&timenow); |
1297 | ||
1298 | ifnet_lock_exclusive(ifp); | |
9bccf70c A |
1299 | TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) |
1300 | { | |
1301 | struct in6_ifaddr *ifa6; | |
1302 | int ifa_plen; | |
1303 | u_int32_t storedlifetime; | |
1c79356b | 1304 | |
9bccf70c A |
1305 | if (ifa->ifa_addr->sa_family != AF_INET6) |
1306 | continue; | |
1c79356b | 1307 | |
9bccf70c | 1308 | ifa6 = (struct in6_ifaddr *)ifa; |
1c79356b | 1309 | |
9bccf70c A |
1310 | /* |
1311 | * Spec is not clear here, but I believe we should concentrate | |
1312 | * on unicast (i.e. not anycast) addresses. | |
1313 | * XXX: other ia6_flags? detached or duplicated? | |
1314 | */ | |
1315 | if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) | |
1316 | continue; | |
1317 | ||
1318 | ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL); | |
1319 | if (ifa_plen != new->ndpr_plen || | |
1320 | !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, | |
1321 | &new->ndpr_prefix.sin6_addr, | |
1322 | ifa_plen)) | |
1323 | continue; | |
1c79356b | 1324 | |
9bccf70c A |
1325 | if (ia6_match == NULL) /* remember the first one */ |
1326 | ia6_match = ifa6; | |
1c79356b | 1327 | |
9bccf70c A |
1328 | if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0) |
1329 | continue; | |
1c79356b | 1330 | |
9bccf70c A |
1331 | /* |
1332 | * An already autoconfigured address matched. Now that we | |
1333 | * are sure there is at least one matched address, we can | |
1334 | * proceed to 5.5.3. (e): update the lifetimes according to the | |
1335 | * "two hours" rule and the privacy extension. | |
1336 | */ | |
1337 | #define TWOHOUR (120*60) | |
1338 | lt6_tmp = ifa6->ia6_lifetime; | |
1339 | ||
1340 | storedlifetime = IFA6_IS_INVALID(ifa6) ? 0 : | |
91447636 | 1341 | (lt6_tmp.ia6t_expire - timenow.tv_sec); |
1c79356b | 1342 | |
9bccf70c A |
1343 | if (TWOHOUR < new->ndpr_vltime || |
1344 | storedlifetime < new->ndpr_vltime) { | |
1345 | lt6_tmp.ia6t_vltime = new->ndpr_vltime; | |
1346 | } else if (storedlifetime <= TWOHOUR | |
1347 | #if 0 | |
1348 | /* | |
1349 | * This condition is logically redundant, so we just | |
1350 | * omit it. | |
1351 | * See IPng 6712, 6717, and 6721. | |
1352 | */ | |
1353 | && new->ndpr_vltime <= storedlifetime | |
1354 | #endif | |
1355 | ) { | |
1356 | if (auth) { | |
1357 | lt6_tmp.ia6t_vltime = new->ndpr_vltime; | |
1358 | } | |
1359 | } else { | |
1c79356b | 1360 | /* |
9bccf70c A |
1361 | * new->ndpr_vltime <= TWOHOUR && |
1362 | * TWOHOUR < storedlifetime | |
1c79356b | 1363 | */ |
9bccf70c | 1364 | lt6_tmp.ia6t_vltime = TWOHOUR; |
1c79356b | 1365 | } |
1c79356b | 1366 | |
9bccf70c A |
1367 | /* The 2 hour rule is not imposed for preferred lifetime. */ |
1368 | lt6_tmp.ia6t_pltime = new->ndpr_pltime; | |
1c79356b | 1369 | |
9bccf70c | 1370 | in6_init_address_ltimes(pr, <6_tmp); |
1c79356b A |
1371 | |
1372 | /* | |
9bccf70c A |
1373 | * When adjusting the lifetimes of an existing temporary |
1374 | * address, only lower the lifetimes. | |
1375 | * RFC 3041 3.3. (1). | |
1376 | * XXX: how should we modify ia6t_[pv]ltime? | |
1c79356b | 1377 | */ |
9bccf70c A |
1378 | if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { |
1379 | if (lt6_tmp.ia6t_expire == 0 || /* no expire */ | |
1380 | lt6_tmp.ia6t_expire > | |
1381 | ifa6->ia6_lifetime.ia6t_expire) { | |
1382 | lt6_tmp.ia6t_expire = | |
1383 | ifa6->ia6_lifetime.ia6t_expire; | |
1384 | } | |
1385 | if (lt6_tmp.ia6t_preferred == 0 || /* no expire */ | |
1386 | lt6_tmp.ia6t_preferred > | |
1387 | ifa6->ia6_lifetime.ia6t_preferred) { | |
1388 | lt6_tmp.ia6t_preferred = | |
1389 | ifa6->ia6_lifetime.ia6t_preferred; | |
1390 | } | |
1c79356b | 1391 | } |
1c79356b | 1392 | |
9bccf70c A |
1393 | ifa6->ia6_lifetime = lt6_tmp; |
1394 | } | |
91447636 | 1395 | ifnet_lock_done(ifp); |
9bccf70c A |
1396 | if (ia6_match == NULL && new->ndpr_vltime) { |
1397 | /* | |
1398 | * No address matched and the valid lifetime is non-zero. | |
1399 | * Create a new address. | |
1400 | */ | |
1401 | if ((ia6 = in6_ifadd(new, NULL)) != NULL) { | |
1402 | /* | |
1403 | * note that we should use pr (not new) for reference. | |
1404 | */ | |
2d21ac55 | 1405 | lck_mtx_lock(nd6_mutex); |
9bccf70c | 1406 | pr->ndpr_refcnt++; |
2d21ac55 | 1407 | lck_mtx_unlock(nd6_mutex); |
9bccf70c | 1408 | ia6->ia6_ndpr = pr; |
1c79356b | 1409 | |
9bccf70c A |
1410 | #if 0 |
1411 | /* XXXYYY Don't do this, according to Jinmei. */ | |
1412 | pr->ndpr_addr = new->ndpr_addr; | |
1413 | #endif | |
1c79356b | 1414 | |
9bccf70c A |
1415 | /* |
1416 | * RFC 3041 3.3 (2). | |
1417 | * When a new public address is created as described | |
1418 | * in RFC2462, also create a new temporary address. | |
1419 | * | |
1420 | * RFC 3041 3.5. | |
1421 | * When an interface connects to a new link, a new | |
1422 | * randomized interface identifier should be generated | |
1423 | * immediately together with a new set of temporary | |
1424 | * addresses. Thus, we specifiy 1 as the 2nd arg of | |
1425 | * in6_tmpifadd(). | |
1426 | */ | |
1427 | if (ip6_use_tempaddr) { | |
1428 | int e; | |
b0d623f7 | 1429 | if ((e = in6_tmpifadd(ia6, 1, M_NOWAIT)) != 0) { |
9bccf70c A |
1430 | nd6log((LOG_NOTICE, "prelist_update: " |
1431 | "failed to create a temporary " | |
1432 | "address, errno=%d\n", | |
1433 | e)); | |
1434 | } | |
1435 | } | |
b0d623f7 A |
1436 | ifafree(&ia6->ia_ifa); |
1437 | ia6 = NULL; | |
1c79356b | 1438 | |
1c79356b | 1439 | /* |
9bccf70c A |
1440 | * A newly added address might affect the status |
1441 | * of other addresses, so we check and update it. | |
1442 | * XXX: what if address duplication happens? | |
1c79356b | 1443 | */ |
91447636 | 1444 | pfxlist_onlink_check(0); |
9bccf70c A |
1445 | } else { |
1446 | /* just set an error. do not bark here. */ | |
1447 | error = EADDRNOTAVAIL; /* XXX: might be unused. */ | |
1c79356b | 1448 | } |
1c79356b A |
1449 | } |
1450 | ||
2d21ac55 A |
1451 | afteraddrconf: |
1452 | ||
1453 | end: | |
1454 | if (pr != NULL) | |
1455 | ndpr_rele(pr, FALSE); | |
9bccf70c | 1456 | |
1c79356b A |
1457 | return error; |
1458 | } | |
1459 | ||
1460 | /* | |
1461 | * A supplement function used in the on-link detection below; | |
1462 | * detect if a given prefix has a (probably) reachable advertising router. | |
1463 | * XXX: lengthy function name... | |
1464 | */ | |
9bccf70c | 1465 | static struct nd_pfxrouter * |
91447636 A |
1466 | find_pfxlist_reachable_router( |
1467 | struct nd_prefix *pr) | |
1c79356b A |
1468 | { |
1469 | struct nd_pfxrouter *pfxrtr; | |
1470 | struct rtentry *rt; | |
1471 | struct llinfo_nd6 *ln; | |
1472 | ||
91447636 A |
1473 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
1474 | ||
1c79356b A |
1475 | for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr; |
1476 | pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) { | |
b0d623f7 | 1477 | /* Callee returns a locked route upon success */ |
1c79356b | 1478 | if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0, |
b0d623f7 A |
1479 | pfxrtr->router->ifp, 0)) != NULL) { |
1480 | RT_LOCK_ASSERT_HELD(rt); | |
1481 | if ((ln = rt->rt_llinfo) != NULL && | |
1482 | ND6_IS_LLINFO_PROBREACH(ln)) { | |
1483 | RT_REMREF_LOCKED(rt); | |
1484 | RT_UNLOCK(rt); | |
1485 | break; /* found */ | |
1486 | } | |
1487 | RT_REMREF_LOCKED(rt); | |
1488 | RT_UNLOCK(rt); | |
1489 | } | |
1c79356b A |
1490 | } |
1491 | ||
1492 | return(pfxrtr); | |
1493 | ||
1494 | } | |
1495 | ||
1496 | /* | |
1497 | * Check if each prefix in the prefix list has at least one available router | |
9bccf70c A |
1498 | * that advertised the prefix (a router is "available" if its neighbor cache |
1499 | * entry is reachable or probably reachable). | |
1c79356b A |
1500 | * If the check fails, the prefix may be off-link, because, for example, |
1501 | * we have moved from the network but the lifetime of the prefix has not | |
9bccf70c A |
1502 | * expired yet. So we should not use the prefix if there is another prefix |
1503 | * that has an available router. | |
1504 | * But, if there is no prefix that has an available router, we still regards | |
1505 | * all the prefixes as on-link. This is because we can't tell if all the | |
1c79356b A |
1506 | * routers are simply dead or if we really moved from the network and there |
1507 | * is no router around us. | |
1508 | */ | |
1509 | void | |
91447636 | 1510 | pfxlist_onlink_check(int nd6locked) |
1c79356b A |
1511 | { |
1512 | struct nd_prefix *pr; | |
9bccf70c | 1513 | struct in6_ifaddr *ifa; |
1c79356b A |
1514 | |
1515 | /* | |
1516 | * Check if there is a prefix that has a reachable advertising | |
1517 | * router. | |
1518 | */ | |
91447636 A |
1519 | if (nd6locked == 0) |
1520 | lck_mtx_lock(nd6_mutex); | |
1521 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); | |
1c79356b | 1522 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { |
9bccf70c | 1523 | if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) |
1c79356b A |
1524 | break; |
1525 | } | |
1526 | ||
1527 | if (pr) { | |
1528 | /* | |
1529 | * There is at least one prefix that has a reachable router. | |
9bccf70c A |
1530 | * Detach prefixes which have no reachable advertising |
1531 | * router, and attach other prefixes. | |
1c79356b A |
1532 | */ |
1533 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
9bccf70c A |
1534 | /* XXX: a link-local prefix should never be detached */ |
1535 | if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) | |
1536 | continue; | |
1537 | ||
1538 | /* | |
1539 | * we aren't interested in prefixes without the L bit | |
1540 | * set. | |
1541 | */ | |
1542 | if (pr->ndpr_raf_onlink == 0) | |
1543 | continue; | |
1544 | ||
1545 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && | |
1546 | find_pfxlist_reachable_router(pr) == NULL) | |
1547 | pr->ndpr_stateflags |= NDPRF_DETACHED; | |
1548 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && | |
1549 | find_pfxlist_reachable_router(pr) != 0) | |
1550 | pr->ndpr_stateflags &= ~NDPRF_DETACHED; | |
1c79356b | 1551 | } |
9bccf70c A |
1552 | } else { |
1553 | /* there is no prefix that has a reachable router */ | |
1c79356b | 1554 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { |
9bccf70c A |
1555 | if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) |
1556 | continue; | |
1557 | ||
1558 | if (pr->ndpr_raf_onlink == 0) | |
1559 | continue; | |
1560 | ||
1561 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) | |
1562 | pr->ndpr_stateflags &= ~NDPRF_DETACHED; | |
1563 | } | |
1564 | } | |
1565 | ||
1566 | /* | |
1567 | * Remove each interface route associated with a (just) detached | |
1568 | * prefix, and reinstall the interface route for a (just) attached | |
1569 | * prefix. Note that all attempt of reinstallation does not | |
1570 | * necessarily success, when a same prefix is shared among multiple | |
1571 | * interfaces. Such cases will be handled in nd6_prefix_onlink, | |
1572 | * so we don't have to care about them. | |
1573 | */ | |
1574 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
1575 | int e; | |
1576 | ||
1577 | if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) | |
1578 | continue; | |
1579 | ||
1580 | if (pr->ndpr_raf_onlink == 0) | |
1581 | continue; | |
1582 | ||
1583 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && | |
1584 | (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { | |
1585 | if ((e = nd6_prefix_offlink(pr)) != 0) { | |
1586 | nd6log((LOG_ERR, | |
1587 | "pfxlist_onlink_check: failed to " | |
1588 | "make %s/%d offlink, errno=%d\n", | |
1589 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
1590 | pr->ndpr_plen, e)); | |
1591 | } | |
1592 | } | |
1593 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && | |
1594 | (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && | |
1595 | pr->ndpr_raf_onlink) { | |
91447636 | 1596 | if ((e = nd6_prefix_onlink(pr, 0, 1)) != 0) { |
9bccf70c A |
1597 | nd6log((LOG_ERR, |
1598 | "pfxlist_onlink_check: failed to " | |
1599 | "make %s/%d offlink, errno=%d\n", | |
1600 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
1601 | pr->ndpr_plen, e)); | |
1602 | } | |
1603 | } | |
1604 | } | |
1605 | ||
1606 | /* | |
1607 | * Changes on the prefix status might affect address status as well. | |
1608 | * Make sure that all addresses derived from an attached prefix are | |
1609 | * attached, and that all addresses derived from a detached prefix are | |
1610 | * detached. Note, however, that a manually configured address should | |
1611 | * always be attached. | |
1612 | * The precise detection logic is same as the one for prefixes. | |
1613 | */ | |
91447636 | 1614 | for (ifa = in6_ifaddrs; ifa; ifa = ifa->ia_next) { |
9bccf70c A |
1615 | if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) |
1616 | continue; | |
1617 | ||
1618 | if (ifa->ia6_ndpr == NULL) { | |
1619 | /* | |
1620 | * This can happen when we first configure the address | |
1621 | * (i.e. the address exists, but the prefix does not). | |
1622 | * XXX: complicated relationships... | |
1623 | */ | |
1624 | continue; | |
1625 | } | |
1626 | ||
1627 | if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) | |
1628 | break; | |
1629 | } | |
1630 | if (ifa) { | |
91447636 | 1631 | for (ifa = in6_ifaddrs; ifa; ifa = ifa->ia_next) { |
9bccf70c A |
1632 | if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) |
1633 | continue; | |
1634 | ||
1635 | if (ifa->ia6_ndpr == NULL) /* XXX: see above. */ | |
1636 | continue; | |
1637 | ||
1638 | if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) | |
1639 | ifa->ia6_flags &= ~IN6_IFF_DETACHED; | |
1640 | else | |
1641 | ifa->ia6_flags |= IN6_IFF_DETACHED; | |
1c79356b A |
1642 | } |
1643 | } | |
1644 | else { | |
91447636 | 1645 | for (ifa = in6_ifaddrs; ifa; ifa = ifa->ia_next) { |
9bccf70c A |
1646 | if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0) |
1647 | continue; | |
1648 | ||
1649 | ifa->ia6_flags &= ~IN6_IFF_DETACHED; | |
1650 | } | |
1c79356b | 1651 | } |
91447636 A |
1652 | if (nd6locked == 0) |
1653 | lck_mtx_unlock(nd6_mutex); | |
1c79356b A |
1654 | } |
1655 | ||
9bccf70c | 1656 | int |
91447636 A |
1657 | nd6_prefix_onlink( |
1658 | struct nd_prefix *pr, int rtlocked, int nd6locked) | |
1c79356b | 1659 | { |
9bccf70c A |
1660 | struct ifaddr *ifa; |
1661 | struct ifnet *ifp = pr->ndpr_ifp; | |
1662 | struct sockaddr_in6 mask6; | |
1663 | struct nd_prefix *opr; | |
b0d623f7 | 1664 | u_int32_t rtflags; |
9bccf70c A |
1665 | int error = 0; |
1666 | struct rtentry *rt = NULL; | |
1667 | ||
1668 | /* sanity check */ | |
1669 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { | |
1670 | nd6log((LOG_ERR, | |
1671 | "nd6_prefix_onlink: %s/%d is already on-link\n", | |
1672 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen); | |
1673 | return(EEXIST)); | |
1674 | } | |
1675 | ||
1676 | /* | |
1677 | * Add the interface route associated with the prefix. Before | |
1678 | * installing the route, check if there's the same prefix on another | |
1679 | * interface, and the prefix has already installed the interface route. | |
1680 | * Although such a configuration is expected to be rare, we explicitly | |
1681 | * allow it. | |
1682 | */ | |
91447636 A |
1683 | if (nd6locked == 0) |
1684 | lck_mtx_lock(nd6_mutex); | |
1685 | else | |
1686 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); | |
9bccf70c A |
1687 | for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { |
1688 | if (opr == pr) | |
1689 | continue; | |
1690 | ||
1691 | if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) | |
1692 | continue; | |
1693 | ||
1694 | if (opr->ndpr_plen == pr->ndpr_plen && | |
1695 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, | |
1696 | &opr->ndpr_prefix.sin6_addr, | |
91447636 A |
1697 | pr->ndpr_plen)) { |
1698 | if (nd6locked == 0) | |
1699 | lck_mtx_unlock(nd6_mutex); | |
9bccf70c | 1700 | return(0); |
91447636 | 1701 | } |
9bccf70c A |
1702 | } |
1703 | ||
91447636 A |
1704 | if (nd6locked == 0) |
1705 | lck_mtx_unlock(nd6_mutex); | |
9bccf70c A |
1706 | /* |
1707 | * We prefer link-local addresses as the associated interface address. | |
1708 | */ | |
1709 | /* search for a link-local addr */ | |
1710 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, | |
1711 | IN6_IFF_NOTREADY| | |
1712 | IN6_IFF_ANYCAST); | |
1713 | if (ifa == NULL) { | |
1714 | /* XXX: freebsd does not have ifa_ifwithaf */ | |
91447636 | 1715 | ifnet_lock_exclusive(ifp); |
9bccf70c A |
1716 | TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) |
1717 | { | |
1718 | if (ifa->ifa_addr->sa_family == AF_INET6) | |
1719 | break; | |
1720 | } | |
b0d623f7 A |
1721 | if (ifa != NULL) |
1722 | ifaref(ifa); | |
91447636 | 1723 | ifnet_lock_done(ifp); |
9bccf70c A |
1724 | /* should we care about ia6_flags? */ |
1725 | } | |
1726 | if (ifa == NULL) { | |
1727 | /* | |
1728 | * This can still happen, when, for example, we receive an RA | |
1729 | * containing a prefix with the L bit set and the A bit clear, | |
1730 | * after removing all IPv6 addresses on the receiving | |
1731 | * interface. This should, of course, be rare though. | |
1732 | */ | |
1733 | nd6log((LOG_NOTICE, | |
1734 | "nd6_prefix_onlink: failed to find any ifaddr" | |
1735 | " to add route for a prefix(%s/%d) on %s\n", | |
1736 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
1737 | pr->ndpr_plen, if_name(ifp))); | |
1738 | return(0); | |
1739 | } | |
1c79356b A |
1740 | |
1741 | /* | |
9bccf70c A |
1742 | * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. |
1743 | * ifa->ifa_rtrequest = nd6_rtrequest; | |
1c79356b | 1744 | */ |
9bccf70c A |
1745 | bzero(&mask6, sizeof(mask6)); |
1746 | mask6.sin6_len = sizeof(mask6); | |
1747 | mask6.sin6_addr = pr->ndpr_mask; | |
91447636 A |
1748 | |
1749 | if (rtlocked == 0) | |
b0d623f7 | 1750 | lck_mtx_lock(rnh_lock); |
91447636 | 1751 | |
9bccf70c A |
1752 | rtflags = ifa->ifa_flags | RTF_CLONING | RTF_UP; |
1753 | if (nd6_need_cache(ifp)) { | |
1754 | /* explicitly set in case ifa_flags does not set the flag. */ | |
1755 | rtflags |= RTF_CLONING; | |
1756 | } else { | |
1757 | /* | |
1758 | * explicitly clear the cloning bit in case ifa_flags sets it. | |
1759 | */ | |
1760 | rtflags &= ~RTF_CLONING; | |
1761 | } | |
91447636 | 1762 | error = rtrequest_locked(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, |
9bccf70c A |
1763 | ifa->ifa_addr, (struct sockaddr *)&mask6, |
1764 | rtflags, &rt); | |
1765 | if (error == 0) { | |
b0d623f7 A |
1766 | if (rt != NULL) { /* this should be non NULL, though */ |
1767 | RT_LOCK(rt); | |
9bccf70c | 1768 | nd6_rtmsg(RTM_ADD, rt); |
b0d623f7 A |
1769 | RT_UNLOCK(rt); |
1770 | } | |
9bccf70c A |
1771 | pr->ndpr_stateflags |= NDPRF_ONLINK; |
1772 | } | |
1773 | else { | |
1774 | nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a" | |
1775 | " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx " | |
1776 | "errno = %d\n", | |
1777 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), | |
1778 | pr->ndpr_plen, if_name(ifp), | |
1779 | ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), | |
1780 | ip6_sprintf(&mask6.sin6_addr), rtflags, error)); | |
1781 | } | |
1782 | ||
1783 | if (rt != NULL) | |
b0d623f7 | 1784 | RT_REMREF(rt); |
9bccf70c | 1785 | |
91447636 | 1786 | if (rtlocked == 0) |
b0d623f7 A |
1787 | lck_mtx_unlock(rnh_lock); |
1788 | ||
1789 | ifafree(ifa); | |
1790 | ||
9bccf70c A |
1791 | return(error); |
1792 | } | |
1793 | ||
1794 | int | |
91447636 A |
1795 | nd6_prefix_offlink( |
1796 | struct nd_prefix *pr) | |
9bccf70c A |
1797 | { |
1798 | int error = 0; | |
1799 | struct ifnet *ifp = pr->ndpr_ifp; | |
1800 | struct nd_prefix *opr; | |
1801 | struct sockaddr_in6 sa6, mask6; | |
1802 | struct rtentry *rt = NULL; | |
1803 | ||
1804 | /* sanity check */ | |
1805 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { | |
1806 | nd6log((LOG_ERR, | |
1807 | "nd6_prefix_offlink: %s/%d is already off-link\n", | |
1808 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen)); | |
1809 | return(EEXIST); | |
1810 | } | |
1811 | ||
1c79356b A |
1812 | bzero(&sa6, sizeof(sa6)); |
1813 | sa6.sin6_family = AF_INET6; | |
1814 | sa6.sin6_len = sizeof(sa6); | |
1815 | bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, | |
1816 | sizeof(struct in6_addr)); | |
1817 | bzero(&mask6, sizeof(mask6)); | |
1818 | mask6.sin6_family = AF_INET6; | |
1819 | mask6.sin6_len = sizeof(sa6); | |
1820 | bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); | |
b0d623f7 | 1821 | lck_mtx_lock(rnh_lock); |
91447636 | 1822 | error = rtrequest_locked(RTM_DELETE, (struct sockaddr *)&sa6, NULL, |
9bccf70c A |
1823 | (struct sockaddr *)&mask6, 0, &rt); |
1824 | if (error == 0) { | |
1825 | pr->ndpr_stateflags &= ~NDPRF_ONLINK; | |
1c79356b | 1826 | |
9bccf70c | 1827 | /* report the route deletion to the routing socket. */ |
b0d623f7 A |
1828 | if (rt != NULL) { |
1829 | RT_LOCK(rt); | |
9bccf70c | 1830 | nd6_rtmsg(RTM_DELETE, rt); |
b0d623f7 A |
1831 | RT_UNLOCK(rt); |
1832 | } | |
1c79356b | 1833 | |
9bccf70c A |
1834 | /* |
1835 | * There might be the same prefix on another interface, | |
1836 | * the prefix which could not be on-link just because we have | |
1837 | * the interface route (see comments in nd6_prefix_onlink). | |
1838 | * If there's one, try to make the prefix on-link on the | |
1839 | * interface. | |
1840 | */ | |
91447636 | 1841 | lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED); |
9bccf70c A |
1842 | for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) { |
1843 | if (opr == pr) | |
1844 | continue; | |
1c79356b | 1845 | |
9bccf70c A |
1846 | if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) |
1847 | continue; | |
1c79356b | 1848 | |
9bccf70c A |
1849 | /* |
1850 | * KAME specific: detached prefixes should not be | |
1851 | * on-link. | |
1852 | */ | |
1853 | if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) | |
1854 | continue; | |
1855 | ||
1856 | if (opr->ndpr_plen == pr->ndpr_plen && | |
1857 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, | |
1858 | &opr->ndpr_prefix.sin6_addr, | |
1859 | pr->ndpr_plen)) { | |
1860 | int e; | |
1861 | ||
91447636 | 1862 | if ((e = nd6_prefix_onlink(opr, 1, 1)) != 0) { |
9bccf70c A |
1863 | nd6log((LOG_ERR, |
1864 | "nd6_prefix_offlink: failed to " | |
1865 | "recover a prefix %s/%d from %s " | |
1866 | "to %s (errno = %d)\n", | |
1867 | ip6_sprintf(&opr->ndpr_prefix.sin6_addr), | |
1868 | opr->ndpr_plen, if_name(ifp), | |
1869 | if_name(opr->ndpr_ifp), e)); | |
1870 | } | |
1871 | } | |
1872 | } | |
1c79356b A |
1873 | } |
1874 | else { | |
9bccf70c A |
1875 | /* XXX: can we still set the NDPRF_ONLINK flag? */ |
1876 | nd6log((LOG_ERR, | |
1877 | "nd6_prefix_offlink: failed to delete route: " | |
1878 | "%s/%d on %s (errno = %d)\n", | |
1879 | ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, if_name(ifp), | |
1880 | error)); | |
1c79356b A |
1881 | } |
1882 | ||
2d21ac55 A |
1883 | if (rt != NULL) |
1884 | rtfree_locked(rt); | |
1885 | ||
b0d623f7 | 1886 | lck_mtx_unlock(rnh_lock); |
9bccf70c A |
1887 | |
1888 | return(error); | |
1c79356b A |
1889 | } |
1890 | ||
1891 | static struct in6_ifaddr * | |
91447636 A |
1892 | in6_ifadd( |
1893 | struct nd_prefix *pr, | |
1894 | struct in6_addr *ifid) /* Mobile IPv6 addition */ | |
1c79356b | 1895 | { |
9bccf70c | 1896 | struct ifnet *ifp = pr->ndpr_ifp; |
1c79356b | 1897 | struct ifaddr *ifa; |
9bccf70c A |
1898 | struct in6_aliasreq ifra; |
1899 | struct in6_ifaddr *ia, *ib; | |
1900 | int error, plen0; | |
1c79356b | 1901 | struct in6_addr mask; |
9bccf70c | 1902 | int prefixlen = pr->ndpr_plen; |
1c79356b A |
1903 | |
1904 | in6_len2mask(&mask, prefixlen); | |
1905 | ||
9bccf70c A |
1906 | /* |
1907 | * find a link-local address (will be interface ID). | |
1908 | * Is it really mandatory? Theoretically, a global or a site-local | |
1909 | * address can be configured without a link-local address, if we | |
1910 | * have a unique interface identifier... | |
1911 | * | |
1912 | * it is not mandatory to have a link-local address, we can generate | |
1913 | * interface identifier on the fly. we do this because: | |
1914 | * (1) it should be the easiest way to find interface identifier. | |
1915 | * (2) RFC2462 5.4 suggesting the use of the same interface identifier | |
1916 | * for multiple addresses on a single interface, and possible shortcut | |
1917 | * of DAD. we omitted DAD for this reason in the past. | |
1918 | * (3) a user can prevent autoconfiguration of global address | |
1919 | * by removing link-local address by hand (this is partly because we | |
1920 | * don't have other way to control the use of IPv6 on a interface. | |
1921 | * this has been our design choice - cf. NRL's "ifconfig auto"). | |
1922 | * (4) it is easier to manage when an interface has addresses | |
1923 | * with the same interface identifier, than to have multiple addresses | |
1924 | * with different interface identifiers. | |
1925 | * | |
1926 | * Mobile IPv6 addition: allow for caller to specify a wished interface | |
1927 | * ID. This is to not break connections when moving addresses between | |
1928 | * interfaces. | |
1929 | */ | |
1c79356b A |
1930 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ |
1931 | if (ifa) | |
1932 | ib = (struct in6_ifaddr *)ifa; | |
1933 | else | |
1934 | return NULL; | |
1935 | ||
1936 | #if 0 /* don't care link local addr state, and always do DAD */ | |
1937 | /* if link-local address is not eligible, do not autoconfigure. */ | |
1938 | if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) { | |
1939 | printf("in6_ifadd: link-local address not ready\n"); | |
b0d623f7 | 1940 | ifafree(ifa); |
1c79356b A |
1941 | return NULL; |
1942 | } | |
1943 | #endif | |
1944 | ||
1945 | /* prefixlen + ifidlen must be equal to 128 */ | |
9bccf70c A |
1946 | plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); |
1947 | if (prefixlen != plen0) { | |
1948 | nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s " | |
1949 | "(prefix=%d ifid=%d)\n", | |
1950 | if_name(ifp), prefixlen, 128 - plen0)); | |
b0d623f7 | 1951 | ifafree(ifa); |
1c79356b A |
1952 | return NULL; |
1953 | } | |
1954 | ||
1955 | /* make ifaddr */ | |
1c79356b | 1956 | |
9bccf70c A |
1957 | bzero(&ifra, sizeof(ifra)); |
1958 | /* | |
1959 | * in6_update_ifa() does not use ifra_name, but we accurately set it | |
1960 | * for safety. | |
1961 | */ | |
1962 | strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); | |
1963 | ifra.ifra_addr.sin6_family = AF_INET6; | |
1964 | ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); | |
1965 | /* prefix */ | |
1966 | bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr, | |
1967 | sizeof(ifra.ifra_addr.sin6_addr)); | |
1968 | ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; | |
1969 | ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; | |
1970 | ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; | |
1971 | ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; | |
1c79356b | 1972 | |
9bccf70c A |
1973 | /* interface ID */ |
1974 | if (ifid == NULL || IN6_IS_ADDR_UNSPECIFIED(ifid)) | |
1975 | ifid = &ib->ia_addr.sin6_addr; | |
1976 | ifra.ifra_addr.sin6_addr.s6_addr32[0] | |
1977 | |= (ifid->s6_addr32[0] & ~mask.s6_addr32[0]); | |
1978 | ifra.ifra_addr.sin6_addr.s6_addr32[1] | |
1979 | |= (ifid->s6_addr32[1] & ~mask.s6_addr32[1]); | |
1980 | ifra.ifra_addr.sin6_addr.s6_addr32[2] | |
1981 | |= (ifid->s6_addr32[2] & ~mask.s6_addr32[2]); | |
1982 | ifra.ifra_addr.sin6_addr.s6_addr32[3] | |
1983 | |= (ifid->s6_addr32[3] & ~mask.s6_addr32[3]); | |
1984 | ||
1985 | /* new prefix mask. */ | |
1986 | ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); | |
1987 | ifra.ifra_prefixmask.sin6_family = AF_INET6; | |
1988 | bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, | |
1989 | sizeof(ifra.ifra_prefixmask.sin6_addr)); | |
1c79356b | 1990 | |
9bccf70c A |
1991 | /* |
1992 | * lifetime. | |
1993 | * XXX: in6_init_address_ltimes would override these values later. | |
1994 | * We should reconsider this logic. | |
1995 | */ | |
1996 | ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; | |
1997 | ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; | |
1c79356b | 1998 | |
9bccf70c A |
1999 | /* XXX: scope zone ID? */ |
2000 | ||
2001 | ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ | |
2002 | /* | |
2003 | * temporarily set the nopfx flag to avoid conflict. | |
2004 | * XXX: we should reconsider the entire mechanism about prefix | |
2005 | * manipulation. | |
2006 | */ | |
2007 | ifra.ifra_flags |= IN6_IFF_NOPFX; | |
2008 | ||
2009 | /* | |
2010 | * keep the new address, regardless of the result of in6_update_ifa. | |
2011 | * XXX: this address is now meaningless. | |
2012 | * We should reconsider its role. | |
2013 | */ | |
2014 | pr->ndpr_addr = ifra.ifra_addr.sin6_addr; | |
2015 | ||
b0d623f7 A |
2016 | ifafree(ifa); |
2017 | ifa = NULL; | |
2018 | ||
9bccf70c | 2019 | /* allocate ifaddr structure, link into chain, etc. */ |
b0d623f7 | 2020 | if ((error = in6_update_ifa(ifp, &ifra, NULL, M_NOWAIT)) != 0) { |
9bccf70c A |
2021 | nd6log((LOG_ERR, |
2022 | "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n", | |
2023 | ip6_sprintf(&ifra.ifra_addr.sin6_addr), if_name(ifp), | |
2024 | error)); | |
2025 | return(NULL); /* ifaddr must not have been allocated. */ | |
1c79356b | 2026 | } |
1c79356b | 2027 | |
9bccf70c | 2028 | ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); |
1c79356b | 2029 | |
9bccf70c | 2030 | in6_post_msg(ifp, KEV_INET6_NEW_RTADV_ADDR, ia); |
1c79356b | 2031 | |
9bccf70c A |
2032 | return(ia); /* this must NOT be NULL. */ |
2033 | } | |
1c79356b | 2034 | |
9bccf70c | 2035 | int |
91447636 A |
2036 | in6_tmpifadd( |
2037 | const struct in6_ifaddr *ia0, /* corresponding public address */ | |
b0d623f7 A |
2038 | int forcegen, |
2039 | int how) | |
9bccf70c A |
2040 | { |
2041 | struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; | |
b0d623f7 | 2042 | struct in6_ifaddr *ia, *newia; |
9bccf70c A |
2043 | struct in6_aliasreq ifra; |
2044 | int i, error; | |
2045 | int trylimit = 3; /* XXX: adhoc value */ | |
2046 | u_int32_t randid[2]; | |
2047 | time_t vltime0, pltime0; | |
91447636 A |
2048 | struct timeval timenow; |
2049 | ||
2050 | getmicrotime(&timenow); | |
9bccf70c A |
2051 | |
2052 | bzero(&ifra, sizeof(ifra)); | |
2053 | strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); | |
2054 | ifra.ifra_addr = ia0->ia_addr; | |
2055 | /* copy prefix mask */ | |
2056 | ifra.ifra_prefixmask = ia0->ia_prefixmask; | |
2057 | /* clear the old IFID */ | |
2058 | for (i = 0; i < 4; i++) { | |
2059 | ifra.ifra_addr.sin6_addr.s6_addr32[i] | |
2060 | &= ifra.ifra_prefixmask.sin6_addr.s6_addr32[i]; | |
1c79356b A |
2061 | } |
2062 | ||
9bccf70c A |
2063 | again: |
2064 | in6_get_tmpifid(ifp, (u_int8_t *)randid, | |
2065 | (const u_int8_t *)&ia0->ia_addr.sin6_addr.s6_addr[8], | |
2066 | forcegen); | |
2067 | ifra.ifra_addr.sin6_addr.s6_addr32[2] | |
2068 | |= (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2])); | |
2069 | ifra.ifra_addr.sin6_addr.s6_addr32[3] | |
2070 | |= (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3])); | |
1c79356b A |
2071 | |
2072 | /* | |
9bccf70c A |
2073 | * If by chance the new temporary address is the same as an address |
2074 | * already assigned to the interface, generate a new randomized | |
2075 | * interface identifier and repeat this step. | |
2076 | * RFC 3041 3.3 (4). | |
1c79356b | 2077 | */ |
b0d623f7 A |
2078 | if ((ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr)) != NULL) { |
2079 | ifafree(&ia->ia_ifa); | |
9bccf70c A |
2080 | if (trylimit-- == 0) { |
2081 | nd6log((LOG_NOTICE, "in6_tmpifadd: failed to find " | |
2082 | "a unique random IFID\n")); | |
2083 | return(EEXIST); | |
2084 | } | |
2085 | forcegen = 1; | |
2086 | goto again; | |
1c79356b | 2087 | } |
1c79356b | 2088 | |
9bccf70c A |
2089 | /* |
2090 | * The Valid Lifetime is the lower of the Valid Lifetime of the | |
2091 | * public address or TEMP_VALID_LIFETIME. | |
2092 | * The Preferred Lifetime is the lower of the Preferred Lifetime | |
2093 | * of the public address or TEMP_PREFERRED_LIFETIME - | |
2094 | * DESYNC_FACTOR. | |
2095 | */ | |
2096 | if (ia0->ia6_lifetime.ia6t_expire != 0) { | |
2097 | vltime0 = IFA6_IS_INVALID(ia0) ? 0 : | |
91447636 | 2098 | (ia0->ia6_lifetime.ia6t_expire - timenow.tv_sec); |
9bccf70c A |
2099 | if (vltime0 > ip6_temp_valid_lifetime) |
2100 | vltime0 = ip6_temp_valid_lifetime; | |
2101 | } else | |
2102 | vltime0 = ip6_temp_valid_lifetime; | |
2103 | if (ia0->ia6_lifetime.ia6t_preferred != 0) { | |
2104 | pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 : | |
91447636 | 2105 | (ia0->ia6_lifetime.ia6t_preferred - timenow.tv_sec); |
9bccf70c A |
2106 | if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor){ |
2107 | pltime0 = ip6_temp_preferred_lifetime - | |
2108 | ip6_desync_factor; | |
2109 | } | |
2110 | } else | |
2111 | pltime0 = ip6_temp_preferred_lifetime - ip6_desync_factor; | |
2112 | ifra.ifra_lifetime.ia6t_vltime = vltime0; | |
2113 | ifra.ifra_lifetime.ia6t_pltime = pltime0; | |
1c79356b | 2114 | |
9bccf70c A |
2115 | /* |
2116 | * A temporary address is created only if this calculated Preferred | |
2117 | * Lifetime is greater than REGEN_ADVANCE time units. | |
2118 | */ | |
2119 | if (ifra.ifra_lifetime.ia6t_pltime <= ip6_temp_regen_advance) | |
2120 | return(0); | |
1c79356b | 2121 | |
9bccf70c | 2122 | /* XXX: scope zone ID? */ |
1c79356b | 2123 | |
9bccf70c | 2124 | ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY); |
1c79356b | 2125 | |
9bccf70c | 2126 | /* allocate ifaddr structure, link into chain, etc. */ |
b0d623f7 | 2127 | if ((error = in6_update_ifa(ifp, &ifra, NULL, how)) != 0) |
9bccf70c | 2128 | return(error); |
1c79356b | 2129 | |
9bccf70c A |
2130 | newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); |
2131 | if (newia == NULL) { /* XXX: can it happen? */ | |
2132 | nd6log((LOG_ERR, | |
2133 | "in6_tmpifadd: ifa update succeeded, but we got " | |
2134 | "no ifaddr\n")); | |
2135 | return(EINVAL); /* XXX */ | |
1c79356b | 2136 | } |
2d21ac55 | 2137 | lck_mtx_lock(nd6_mutex); |
9bccf70c A |
2138 | newia->ia6_ndpr = ia0->ia6_ndpr; |
2139 | newia->ia6_ndpr->ndpr_refcnt++; | |
55e303ae A |
2140 | /* |
2141 | * A newly added address might affect the status of other addresses. | |
2142 | * XXX: when the temporary address is generated with a new public | |
2143 | * address, the onlink check is redundant. However, it would be safe | |
2144 | * to do the check explicitly everywhere a new address is generated, | |
2145 | * and, in fact, we surely need the check when we create a new | |
2146 | * temporary address due to deprecation of an old temporary address. | |
2147 | */ | |
2d21ac55 A |
2148 | pfxlist_onlink_check(1); |
2149 | lck_mtx_unlock(nd6_mutex); | |
b0d623f7 | 2150 | ifafree(&newia->ia_ifa); |
55e303ae | 2151 | |
9bccf70c A |
2152 | return(0); |
2153 | } | |
1c79356b A |
2154 | |
2155 | int | |
2156 | in6_init_prefix_ltimes(struct nd_prefix *ndpr) | |
2157 | { | |
91447636 A |
2158 | struct timeval timenow; |
2159 | ||
2160 | getmicrotime(&timenow); | |
9bccf70c | 2161 | /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */ |
1c79356b | 2162 | if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { |
9bccf70c | 2163 | nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" |
1c79356b | 2164 | "(%d) is greater than valid lifetime(%d)\n", |
9bccf70c | 2165 | (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime)); |
1c79356b A |
2166 | return (EINVAL); |
2167 | } | |
2168 | if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) | |
2169 | ndpr->ndpr_preferred = 0; | |
2170 | else | |
91447636 | 2171 | ndpr->ndpr_preferred = timenow.tv_sec + ndpr->ndpr_pltime; |
1c79356b A |
2172 | if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) |
2173 | ndpr->ndpr_expire = 0; | |
2174 | else | |
91447636 | 2175 | ndpr->ndpr_expire = timenow.tv_sec + ndpr->ndpr_vltime; |
1c79356b A |
2176 | |
2177 | return 0; | |
2178 | } | |
2179 | ||
2180 | static void | |
2d21ac55 | 2181 | in6_init_address_ltimes(__unused struct nd_prefix *new, struct in6_addrlifetime *lt6) |
1c79356b | 2182 | { |
91447636 A |
2183 | struct timeval timenow; |
2184 | ||
2185 | getmicrotime(&timenow); | |
1c79356b | 2186 | /* Valid lifetime must not be updated unless explicitly specified. */ |
9bccf70c A |
2187 | /* init ia6t_expire */ |
2188 | if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) | |
2189 | lt6->ia6t_expire = 0; | |
2190 | else { | |
91447636 | 2191 | lt6->ia6t_expire = timenow.tv_sec; |
9bccf70c | 2192 | lt6->ia6t_expire += lt6->ia6t_vltime; |
1c79356b A |
2193 | } |
2194 | ||
2195 | /* init ia6t_preferred */ | |
2196 | if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) | |
2197 | lt6->ia6t_preferred = 0; | |
2198 | else { | |
91447636 | 2199 | lt6->ia6t_preferred = timenow.tv_sec; |
1c79356b A |
2200 | lt6->ia6t_preferred += lt6->ia6t_pltime; |
2201 | } | |
1c79356b A |
2202 | } |
2203 | ||
2204 | /* | |
2205 | * Delete all the routing table entries that use the specified gateway. | |
2206 | * XXX: this function causes search through all entries of routing table, so | |
2207 | * it shouldn't be called when acting as a router. | |
2208 | */ | |
2209 | void | |
91447636 A |
2210 | rt6_flush( |
2211 | struct in6_addr *gateway, | |
2212 | struct ifnet *ifp) | |
1c79356b A |
2213 | { |
2214 | struct radix_node_head *rnh = rt_tables[AF_INET6]; | |
1c79356b A |
2215 | |
2216 | /* We'll care only link-local addresses */ | |
2217 | if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { | |
1c79356b A |
2218 | return; |
2219 | } | |
b0d623f7 | 2220 | lck_mtx_lock(rnh_lock); |
1c79356b A |
2221 | /* XXX: hack for KAME's link-local address kludge */ |
2222 | gateway->s6_addr16[1] = htons(ifp->if_index); | |
2223 | ||
2224 | rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); | |
b0d623f7 | 2225 | lck_mtx_unlock(rnh_lock); |
1c79356b A |
2226 | } |
2227 | ||
2228 | static int | |
91447636 A |
2229 | rt6_deleteroute( |
2230 | struct radix_node *rn, | |
2231 | void *arg) | |
1c79356b A |
2232 | { |
2233 | #define SIN6(s) ((struct sockaddr_in6 *)s) | |
2234 | struct rtentry *rt = (struct rtentry *)rn; | |
2235 | struct in6_addr *gate = (struct in6_addr *)arg; | |
2236 | ||
b0d623f7 | 2237 | lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED); |
91447636 | 2238 | |
b0d623f7 A |
2239 | RT_LOCK(rt); |
2240 | if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) { | |
2241 | RT_UNLOCK(rt); | |
1c79356b | 2242 | return(0); |
b0d623f7 | 2243 | } |
1c79356b | 2244 | |
b0d623f7 A |
2245 | if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr)) { |
2246 | RT_UNLOCK(rt); | |
1c79356b | 2247 | return(0); |
b0d623f7 | 2248 | } |
9bccf70c A |
2249 | /* |
2250 | * Do not delete a static route. | |
2251 | * XXX: this seems to be a bit ad-hoc. Should we consider the | |
2252 | * 'cloned' bit instead? | |
2253 | */ | |
b0d623f7 A |
2254 | if ((rt->rt_flags & RTF_STATIC) != 0) { |
2255 | RT_UNLOCK(rt); | |
9bccf70c | 2256 | return(0); |
b0d623f7 | 2257 | } |
1c79356b A |
2258 | /* |
2259 | * We delete only host route. This means, in particular, we don't | |
2260 | * delete default route. | |
2261 | */ | |
b0d623f7 A |
2262 | if ((rt->rt_flags & RTF_HOST) == 0) { |
2263 | RT_UNLOCK(rt); | |
1c79356b | 2264 | return(0); |
b0d623f7 | 2265 | } |
1c79356b | 2266 | |
b0d623f7 A |
2267 | /* |
2268 | * Safe to drop rt_lock and use rt_key, rt_gateway, since holding | |
2269 | * rnh_lock here prevents another thread from calling rt_setgate() | |
2270 | * on this route. | |
2271 | */ | |
2272 | RT_UNLOCK(rt); | |
2273 | return (rtrequest_locked(RTM_DELETE, rt_key(rt), rt->rt_gateway, | |
2274 | rt_mask(rt), rt->rt_flags, 0)); | |
1c79356b A |
2275 | #undef SIN6 |
2276 | } | |
2277 | ||
2278 | int | |
91447636 A |
2279 | nd6_setdefaultiface( |
2280 | int ifindex) | |
1c79356b A |
2281 | { |
2282 | int error = 0; | |
b0d623f7 | 2283 | ifnet_t def_ifp = NULL; |
1c79356b | 2284 | |
b0d623f7 A |
2285 | ifnet_head_lock_shared(); |
2286 | if (ifindex < 0 || if_index < ifindex) { | |
2287 | ifnet_head_done(); | |
1c79356b | 2288 | return(EINVAL); |
b0d623f7 A |
2289 | } |
2290 | def_ifp = ifindex2ifnet[ifindex]; | |
2291 | ifnet_head_done(); | |
1c79356b | 2292 | |
91447636 | 2293 | lck_mtx_lock(nd6_mutex); |
1c79356b A |
2294 | if (nd6_defifindex != ifindex) { |
2295 | nd6_defifindex = ifindex; | |
2296 | if (nd6_defifindex > 0) | |
b0d623f7 | 2297 | nd6_defifp = def_ifp; |
1c79356b A |
2298 | else |
2299 | nd6_defifp = NULL; | |
2300 | ||
2301 | /* | |
2302 | * If the Default Router List is empty, install a route | |
2303 | * to the specified interface as default or remove the default | |
2304 | * route when the default interface becomes canceled. | |
2305 | * The check for the queue is actually redundant, but | |
2306 | * we do this here to avoid re-install the default route | |
2307 | * if the list is NOT empty. | |
2308 | */ | |
2309 | if (TAILQ_FIRST(&nd_defrouter) == NULL) | |
2310 | defrouter_select(); | |
9bccf70c A |
2311 | |
2312 | /* | |
2313 | * Our current implementation assumes one-to-one maping between | |
2314 | * interfaces and links, so it would be natural to use the | |
2315 | * default interface as the default link. | |
2316 | */ | |
2317 | scope6_setdefault(nd6_defifp); | |
1c79356b A |
2318 | } |
2319 | ||
91447636 | 2320 | lck_mtx_unlock(nd6_mutex); |
1c79356b A |
2321 | return(error); |
2322 | } |