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