]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* $KAME: nd6_rtr.c,v 1.30 2000/03/21 11:37:31 itojun Exp $ */ |
2 | ||
3 | /* | |
4 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. Neither the name of the project nor the names of its contributors | |
16 | * may be used to endorse or promote products derived from this software | |
17 | * without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
29 | * SUCH DAMAGE. | |
30 | */ | |
31 | ||
32 | #include <sys/param.h> | |
33 | #include <sys/systm.h> | |
34 | #include <sys/malloc.h> | |
35 | #include <sys/mbuf.h> | |
36 | #include <sys/socket.h> | |
37 | #include <sys/sockio.h> | |
38 | #include <sys/time.h> | |
39 | #include <sys/kernel.h> | |
40 | #include <sys/errno.h> | |
41 | #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) | |
42 | #include <sys/ioctl.h> | |
43 | #endif | |
44 | #include <sys/syslog.h> | |
45 | ||
46 | #include <net/if.h> | |
47 | #include <net/if_types.h> | |
48 | #include <net/if_dl.h> | |
49 | #include <net/route.h> | |
50 | #include <net/radix.h> | |
51 | ||
52 | #include <netinet/in.h> | |
53 | #include <netinet6/in6_var.h> | |
54 | #include <netinet/ip6.h> | |
55 | #include <netinet6/ip6_var.h> | |
56 | #include <netinet6/nd6.h> | |
57 | #include <netinet/icmp6.h> | |
58 | ||
59 | #if MIP6 | |
60 | #include <netinet6/mip6.h> | |
61 | #endif | |
62 | ||
63 | #include <net/net_osdep.h> | |
64 | ||
65 | #define SDL(s) ((struct sockaddr_dl *)s) | |
66 | ||
67 | static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); | |
68 | static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); | |
69 | /* static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); XXXYYY */ | |
70 | static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, | |
71 | struct in6_addr *, int)); | |
72 | /*static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, | |
73 | struct nd_defrouter *)); XXXYYYY */ | |
74 | static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); | |
75 | static void pfxrtr_del __P((struct nd_pfxrouter *)); | |
76 | /*static struct nd_pfxrouter *find_pfxlist_reachable_router __P((struct nd_prefix *)); XXXYYY */ | |
77 | static void nd6_detach_prefix __P((struct nd_prefix *)); | |
78 | static void nd6_attach_prefix __P((struct nd_prefix *)); | |
79 | /* static void defrouter_addifreq __P((struct ifnet *)); XXXYYY */ | |
80 | ||
81 | static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, | |
82 | struct in6_addrlifetime *lt6, | |
83 | int update_vltime)); | |
84 | ||
85 | static int rt6_deleteroute __P((struct radix_node *, void *)); | |
86 | ||
87 | extern int nd6_recalc_reachtm_interval; | |
88 | ||
89 | struct ifnet *nd6_defifp; | |
90 | int nd6_defifindex; | |
91 | ||
92 | ||
93 | #if MIP6 | |
94 | void (*mip6_select_defrtr_hook)(void) = NULL; | |
95 | struct nd_prefix * (*mip6_get_home_prefix_hook)(void) = NULL; | |
96 | void (*mip6_prelist_update_hook)(struct nd_prefix *pr, | |
97 | struct nd_defrouter *dr) = NULL; | |
98 | void (*mip6_probe_pfxrtrs_hook)(void) = NULL; | |
99 | void (*mip6_store_advint_hook)(struct nd_opt_advint *ai, | |
100 | struct nd_defrouter *dr) = NULL; | |
101 | int (*mip6_get_md_state_hook)(void) = 0; | |
102 | void (*mip6_minus_a_case_hook)(struct nd_prefix *) = NULL; | |
103 | #endif /* MIP6 */ | |
104 | ||
105 | /* | |
106 | * Receive Router Solicitation Message - just for routers. | |
107 | * Router solicitation/advertisement is mostly managed by userland program | |
108 | * (rtadvd) so here we have no function like nd6_ra_output(). | |
109 | * | |
110 | * Based on RFC 2461 | |
111 | */ | |
112 | void | |
113 | nd6_rs_input(m, off, icmp6len) | |
114 | struct mbuf *m; | |
115 | int off, icmp6len; | |
116 | { | |
117 | struct ifnet *ifp = m->m_pkthdr.rcvif; | |
118 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | |
119 | struct nd_router_solicit *nd_rs; | |
120 | struct in6_addr saddr6 = ip6->ip6_src; | |
121 | #if 0 | |
122 | struct in6_addr daddr6 = ip6->ip6_dst; | |
123 | #endif | |
124 | char *lladdr = NULL; | |
125 | int lladdrlen = 0; | |
126 | #if 0 | |
127 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL; | |
128 | struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; | |
129 | struct rtentry *rt = NULL; | |
130 | int is_newentry; | |
131 | #endif | |
132 | union nd_opts ndopts; | |
133 | ||
134 | /* If I'm not a router, ignore it. */ | |
135 | if (ip6_accept_rtadv != 0 || ip6_forwarding != 1) | |
136 | goto freeit; | |
137 | ||
138 | /* Sanity checks */ | |
139 | if (ip6->ip6_hlim != 255) { | |
140 | log(LOG_ERR, | |
141 | "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); | |
142 | goto freeit; | |
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 | |
153 | IP6_EXTHDR_CHECK(m, off, icmp6len,); | |
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) { | |
166 | log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n"); | |
167 | goto freeit; | |
168 | } | |
169 | ||
170 | if (ndopts.nd_opts_src_lladdr) { | |
171 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); | |
172 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; | |
173 | } | |
174 | ||
175 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { | |
176 | log(LOG_INFO, | |
177 | "nd6_rs_input: lladdrlen mismatch for %s " | |
178 | "(if %d, RS packet %d)\n", | |
179 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); | |
180 | } | |
181 | ||
182 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); | |
183 | ||
184 | freeit: | |
185 | m_freem(m); | |
186 | } | |
187 | ||
188 | /* | |
189 | * Receive Router Advertisement Message. | |
190 | * | |
191 | * Based on RFC 2461 | |
192 | * TODO: on-link bit on prefix information | |
193 | * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing | |
194 | */ | |
195 | void | |
196 | nd6_ra_input(m, off, icmp6len) | |
197 | struct mbuf *m; | |
198 | int off, icmp6len; | |
199 | { | |
200 | struct ifnet *ifp = m->m_pkthdr.rcvif; | |
201 | struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; | |
202 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | |
203 | struct nd_router_advert *nd_ra; | |
204 | struct in6_addr saddr6 = ip6->ip6_src; | |
205 | #if 0 | |
206 | struct in6_addr daddr6 = ip6->ip6_dst; | |
207 | int flags; /* = nd_ra->nd_ra_flags_reserved; */ | |
208 | int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0); | |
209 | int is_other = ((flags & ND_RA_FLAG_OTHER) != 0); | |
210 | #endif | |
211 | union nd_opts ndopts; | |
212 | struct nd_defrouter *dr; | |
213 | ||
214 | if (ip6_accept_rtadv == 0) | |
215 | goto freeit; | |
216 | ||
217 | if (ip6->ip6_hlim != 255) { | |
218 | log(LOG_ERR, | |
219 | "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); | |
220 | goto freeit; | |
221 | } | |
222 | ||
223 | if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { | |
224 | log(LOG_ERR, | |
225 | "nd6_ra_input: src %s is not link-local\n", | |
226 | ip6_sprintf(&saddr6)); | |
227 | goto freeit; | |
228 | } | |
229 | ||
230 | #ifndef PULLDOWN_TEST | |
231 | IP6_EXTHDR_CHECK(m, off, icmp6len,); | |
232 | nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); | |
233 | #else | |
234 | IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); | |
235 | if (nd_ra == NULL) { | |
236 | icmp6stat.icp6s_tooshort++; | |
237 | return; | |
238 | } | |
239 | #endif | |
240 | ||
241 | icmp6len -= sizeof(*nd_ra); | |
242 | nd6_option_init(nd_ra + 1, icmp6len, &ndopts); | |
243 | if (nd6_options(&ndopts) < 0) { | |
244 | log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n"); | |
245 | goto freeit; | |
246 | } | |
247 | ||
248 | { | |
249 | struct nd_defrouter dr0; | |
250 | u_int32_t advreachable = nd_ra->nd_ra_reachable; | |
251 | #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__) | |
252 | long time_second = time.tv_sec; | |
253 | #endif | |
254 | ||
255 | dr0.rtaddr = saddr6; | |
256 | dr0.flags = nd_ra->nd_ra_flags_reserved; | |
257 | dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); | |
258 | dr0.expire = time_second + dr0.rtlifetime; | |
259 | dr0.ifp = ifp; | |
260 | dr0.advint = 0; /* Mobile IPv6 */ | |
261 | dr0.advint_expire = 0; /* Mobile IPv6 */ | |
262 | dr0.advints_lost = 0; /* Mobile IPv6 */ | |
263 | /* unspecified or not? (RFC 2461 6.3.4) */ | |
264 | if (advreachable) { | |
265 | NTOHL(advreachable); | |
266 | if (advreachable <= MAX_REACHABLE_TIME && | |
267 | ndi->basereachable != advreachable) { | |
268 | ndi->basereachable = advreachable; | |
269 | ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); | |
270 | ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ | |
271 | } | |
272 | } | |
273 | if (nd_ra->nd_ra_retransmit) | |
274 | ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); | |
275 | if (nd_ra->nd_ra_curhoplimit) | |
276 | ndi->chlim = nd_ra->nd_ra_curhoplimit; | |
277 | dr = defrtrlist_update(&dr0); | |
278 | } | |
279 | ||
280 | /* | |
281 | * prefix | |
282 | */ | |
283 | if (ndopts.nd_opts_pi) { | |
284 | struct nd_opt_hdr *pt; | |
285 | struct nd_opt_prefix_info *pi; | |
286 | struct nd_prefix pr; | |
287 | ||
288 | for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; | |
289 | pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; | |
290 | pt = (struct nd_opt_hdr *)((caddr_t)pt + | |
291 | (pt->nd_opt_len << 3))) { | |
292 | if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) | |
293 | continue; | |
294 | pi = (struct nd_opt_prefix_info *)pt; | |
295 | ||
296 | if (pi->nd_opt_pi_len != 4) { | |
297 | log(LOG_INFO, "nd6_ra_input: invalid option " | |
298 | "len %d for prefix information option, " | |
299 | "ignored\n", pi->nd_opt_pi_len); | |
300 | continue; | |
301 | } | |
302 | ||
303 | if (128 < pi->nd_opt_pi_prefix_len) { | |
304 | log(LOG_INFO, "nd6_ra_input: invalid prefix " | |
305 | "len %d for prefix information option, " | |
306 | "ignored\n", pi->nd_opt_pi_prefix_len); | |
307 | continue; | |
308 | } | |
309 | ||
310 | if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) | |
311 | || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { | |
312 | log(LOG_INFO, "nd6_ra_input: invalid prefix " | |
313 | "%s, ignored\n", | |
314 | ip6_sprintf(&pi->nd_opt_pi_prefix)); | |
315 | continue; | |
316 | } | |
317 | ||
318 | /* aggregatable unicast address, rfc2374 */ | |
319 | if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 | |
320 | && pi->nd_opt_pi_prefix_len != 64) { | |
321 | log(LOG_INFO, "nd6_ra_input: invalid prefixlen " | |
322 | "%d for rfc2374 prefix %s, ignored\n", | |
323 | pi->nd_opt_pi_prefix_len, | |
324 | ip6_sprintf(&pi->nd_opt_pi_prefix)); | |
325 | continue; | |
326 | } | |
327 | ||
328 | bzero(&pr, sizeof(pr)); | |
329 | pr.ndpr_prefix.sin6_family = AF_INET6; | |
330 | pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); | |
331 | pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; | |
332 | pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif; | |
333 | ||
334 | pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & | |
335 | ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; | |
336 | pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & | |
337 | ND_OPT_PI_FLAG_AUTO) ? 1 : 0; | |
338 | pr.ndpr_plen = pi->nd_opt_pi_prefix_len; | |
339 | pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); | |
340 | pr.ndpr_pltime = | |
341 | ntohl(pi->nd_opt_pi_preferred_time); | |
342 | ||
343 | if (in6_init_prefix_ltimes(&pr)) | |
344 | continue; /* prefix lifetime init failed */ | |
345 | ||
346 | (void)prelist_update(&pr, dr, m); | |
347 | } | |
348 | } | |
349 | ||
350 | /* | |
351 | * MTU | |
352 | */ | |
353 | if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { | |
354 | u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); | |
355 | ||
356 | /* lower bound */ | |
357 | if (mtu < IPV6_MMTU) { | |
358 | log(LOG_INFO, "nd6_ra_input: bogus mtu option " | |
359 | "mtu=%d sent from %s, ignoring\n", | |
360 | mtu, ip6_sprintf(&ip6->ip6_src)); | |
361 | goto skip; | |
362 | } | |
363 | ||
364 | /* upper bound */ | |
365 | if (ndi->maxmtu) { | |
366 | if (mtu <= ndi->maxmtu) { | |
367 | int change = (ndi->linkmtu != mtu); | |
368 | ||
369 | ndi->linkmtu = mtu; | |
370 | if (change) /* in6_maxmtu may change */ | |
371 | in6_setmaxmtu(); | |
372 | } else { | |
373 | log(LOG_INFO, "nd6_ra_input: bogus mtu " | |
374 | "mtu=%d sent from %s; " | |
375 | "exceeds maxmtu %d, ignoring\n", | |
376 | mtu, ip6_sprintf(&ip6->ip6_src), | |
377 | ndi->maxmtu); | |
378 | } | |
379 | } else { | |
380 | log(LOG_INFO, "nd6_ra_input: mtu option " | |
381 | "mtu=%d sent from %s; maxmtu unknown, " | |
382 | "ignoring\n", | |
383 | mtu, ip6_sprintf(&ip6->ip6_src)); | |
384 | } | |
385 | } | |
386 | ||
387 | skip: | |
388 | ||
389 | /* | |
390 | * Src linkaddress | |
391 | */ | |
392 | { | |
393 | char *lladdr = NULL; | |
394 | int lladdrlen = 0; | |
395 | ||
396 | if (ndopts.nd_opts_src_lladdr) { | |
397 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); | |
398 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; | |
399 | } | |
400 | ||
401 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { | |
402 | log(LOG_INFO, | |
403 | "nd6_ra_input: lladdrlen mismatch for %s " | |
404 | "(if %d, RA packet %d)\n", | |
405 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); | |
406 | } | |
407 | ||
408 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); | |
409 | ||
410 | /* | |
411 | * Installing a link-layer address might change the state of the | |
412 | * router's neighbor cache, which might also affect our on-link | |
413 | * detection of adveritsed prefixes. | |
414 | */ | |
415 | pfxlist_onlink_check(); | |
416 | } | |
417 | ||
418 | #if MIP6 | |
419 | if (mip6_store_advint_hook) { | |
420 | if (ndopts.nd_opts_adv) | |
421 | (*mip6_store_advint_hook)(ndopts.nd_opts_adv, dr); | |
422 | } | |
423 | #endif | |
424 | ||
425 | freeit: | |
426 | m_freem(m); | |
427 | } | |
428 | ||
429 | /* | |
430 | * default router list proccessing sub routines | |
431 | */ | |
432 | void | |
433 | defrouter_addreq(new) | |
434 | struct nd_defrouter *new; | |
435 | { | |
436 | struct sockaddr_in6 def, mask, gate; | |
437 | int s; | |
438 | ||
439 | Bzero(&def, sizeof(def)); | |
440 | Bzero(&mask, sizeof(mask)); | |
441 | Bzero(&gate, sizeof(gate)); | |
442 | ||
443 | def.sin6_len = mask.sin6_len = gate.sin6_len | |
444 | = sizeof(struct sockaddr_in6); | |
445 | def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; | |
446 | gate.sin6_addr = new->rtaddr; | |
447 | ||
448 | #ifdef __NetBSD__ | |
449 | s = splsoftnet(); | |
450 | #else | |
451 | s = splnet(); | |
452 | #endif | |
453 | (void)rtrequest(RTM_ADD, (struct sockaddr *)&def, | |
454 | (struct sockaddr *)&gate, (struct sockaddr *)&mask, | |
455 | RTF_GATEWAY, NULL); | |
456 | splx(s); | |
457 | return; | |
458 | } | |
459 | ||
460 | /* Add a route to a given interface as default */ | |
461 | void | |
462 | defrouter_addifreq(ifp) | |
463 | struct ifnet *ifp; | |
464 | { | |
465 | struct sockaddr_in6 def, mask; | |
466 | struct ifaddr *ifa; | |
467 | int error, flags; | |
468 | ||
469 | bzero(&def, sizeof(def)); | |
470 | bzero(&mask, sizeof(mask)); | |
471 | ||
472 | def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6); | |
473 | def.sin6_family = mask.sin6_family = AF_INET6; | |
474 | ||
475 | /* | |
476 | * Search for an ifaddr beloging to the specified interface. | |
477 | * XXX: An IPv6 address are required to be assigned on the interface. | |
478 | */ | |
479 | if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) { | |
480 | log(LOG_ERR, /* better error? */ | |
481 | "defrouter_addifreq: failed to find an ifaddr " | |
482 | "to install a route to interface %s\n", | |
483 | if_name(ifp)); | |
484 | return; | |
485 | } | |
486 | ||
487 | flags = ifa->ifa_flags; | |
488 | if ((ifp->if_flags & IFF_POINTOPOINT) != 0) | |
489 | flags &= ~RTF_CLONING; | |
490 | if ((error = rtrequest(RTM_ADD, (struct sockaddr *)&def, | |
491 | ifa->ifa_addr, (struct sockaddr *)&mask, | |
492 | flags, NULL)) != 0) { | |
493 | log(LOG_ERR, | |
494 | "defrouter_addifreq: failed to install a route to " | |
495 | "interface %s (errno = %d)\n", | |
496 | if_name(ifp), error); | |
497 | } | |
498 | } | |
499 | ||
500 | struct nd_defrouter * | |
501 | defrouter_lookup(addr, ifp) | |
502 | struct in6_addr *addr; | |
503 | struct ifnet *ifp; | |
504 | { | |
505 | struct nd_defrouter *dr; | |
506 | ||
507 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; | |
508 | dr = TAILQ_NEXT(dr, dr_entry)) { | |
509 | if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) | |
510 | return(dr); | |
511 | } | |
512 | ||
513 | return(NULL); /* search failed */ | |
514 | } | |
515 | ||
516 | void | |
517 | defrouter_delreq(dr, dofree) | |
518 | struct nd_defrouter *dr; | |
519 | int dofree; | |
520 | { | |
521 | struct sockaddr_in6 def, mask, gate; | |
522 | ||
523 | Bzero(&def, sizeof(def)); | |
524 | Bzero(&mask, sizeof(mask)); | |
525 | Bzero(&gate, sizeof(gate)); | |
526 | ||
527 | def.sin6_len = mask.sin6_len = gate.sin6_len | |
528 | = sizeof(struct sockaddr_in6); | |
529 | def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; | |
530 | gate.sin6_addr = dr->rtaddr; | |
531 | ||
532 | rtrequest(RTM_DELETE, (struct sockaddr *)&def, | |
533 | (struct sockaddr *)&gate, | |
534 | (struct sockaddr *)&mask, | |
535 | RTF_GATEWAY, (struct rtentry **)0); | |
536 | ||
537 | if (dofree) /* XXX: necessary? */ | |
538 | _FREE(dr, M_IP6NDP); | |
539 | } | |
540 | ||
541 | void | |
542 | defrtrlist_del(dr) | |
543 | struct nd_defrouter *dr; | |
544 | { | |
545 | struct nd_defrouter *deldr = NULL; | |
546 | struct nd_prefix *pr; | |
547 | ||
548 | /* | |
549 | * Flush all the routing table entries that use the router | |
550 | * as a next hop. | |
551 | */ | |
552 | if (!ip6_forwarding && ip6_accept_rtadv) { | |
553 | /* above is a good condition? */ | |
554 | rt6_flush(&dr->rtaddr, dr->ifp); | |
555 | } | |
556 | ||
557 | if (dr == TAILQ_FIRST(&nd_defrouter)) | |
558 | deldr = dr; /* The router is primary. */ | |
559 | ||
560 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); | |
561 | ||
562 | /* | |
563 | * Also delete all the pointers to the router in each prefix lists. | |
564 | */ | |
565 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
566 | struct nd_pfxrouter *pfxrtr; | |
567 | if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) | |
568 | pfxrtr_del(pfxrtr); | |
569 | } | |
570 | pfxlist_onlink_check(); | |
571 | ||
572 | /* | |
573 | * If the router is the primary one, choose a new one. | |
574 | * Note that defrouter_select() will remove the current gateway | |
575 | * from the routing table. | |
576 | */ | |
577 | if (deldr) | |
578 | defrouter_select(); | |
579 | ||
580 | _FREE(dr, M_IP6NDP); | |
581 | } | |
582 | ||
583 | /* | |
584 | * Default Router Selection according to Section 6.3.6 of RFC 2461: | |
585 | * 1) Routers that are reachable or probably reachable should be | |
586 | * preferred. | |
587 | * 2) When no routers on the list are known to be reachable or | |
588 | * probably reachable, routers SHOULD be selected in a round-robin | |
589 | * fashion. | |
590 | * 3) If the Default Router List is empty, assume that all | |
591 | * destinations are on-link. | |
592 | */ | |
593 | void | |
594 | defrouter_select() | |
595 | { | |
596 | #ifdef __NetBSD__ | |
597 | int s = splsoftnet(); | |
598 | #else | |
599 | int s = splnet(); | |
600 | #endif | |
601 | struct nd_defrouter *dr, anydr; | |
602 | struct rtentry *rt = NULL; | |
603 | struct llinfo_nd6 *ln = NULL; | |
604 | ||
605 | #if MIP6 | |
606 | /* Mobile IPv6 alternative routine */ | |
607 | if (mip6_select_defrtr_hook) { | |
608 | (*mip6_select_defrtr_hook)(); /* XXXYYY Temporary? */ | |
609 | splx(s); | |
610 | return; | |
611 | } | |
612 | /* End of Mobile IPv6 */ | |
613 | #endif /* MIP6 */ | |
614 | ||
615 | /* | |
616 | * Search for a (probably) reachable router from the list. | |
617 | */ | |
618 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; | |
619 | dr = TAILQ_NEXT(dr, dr_entry)) { | |
620 | if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && | |
621 | (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && | |
622 | ND6_IS_LLINFO_PROBREACH(ln)) { | |
623 | /* Got it, and move it to the head */ | |
624 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); | |
625 | TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry); | |
626 | break; | |
627 | } | |
628 | } | |
629 | ||
630 | if ((dr = TAILQ_FIRST(&nd_defrouter))) { | |
631 | /* | |
632 | * De-install the previous default gateway and install | |
633 | * a new one. | |
634 | * Note that if there is no reachable router in the list, | |
635 | * the head entry will be used anyway. | |
636 | * XXX: do we have to check the current routing table entry? | |
637 | */ | |
638 | bzero(&anydr, sizeof(anydr)); | |
639 | defrouter_delreq(&anydr, 0); | |
640 | defrouter_addreq(dr); | |
641 | } | |
642 | else { | |
643 | /* | |
644 | * The Default Router List is empty, so install the default | |
645 | * route to an inteface. | |
646 | * XXX: The specification does not say this mechanism should | |
647 | * be restricted to hosts, but this would be not useful | |
648 | * (even harmful) for routers. | |
649 | */ | |
650 | if (!ip6_forwarding) { | |
651 | /* | |
652 | * De-install the current default route | |
653 | * in advance. | |
654 | */ | |
655 | bzero(&anydr, sizeof(anydr)); | |
656 | defrouter_delreq(&anydr, 0); | |
657 | if (nd6_defifp) { | |
658 | /* | |
659 | * Install a route to the default interface | |
660 | * as default route. | |
661 | */ | |
662 | defrouter_addifreq(nd6_defifp); | |
663 | } | |
664 | #if ND6_DEBUG | |
665 | else /* noisy log? */ | |
666 | log(LOG_INFO, "defrouter_select: " | |
667 | "there's no default router and no default" | |
668 | " interface\n"); | |
669 | #endif | |
670 | } | |
671 | } | |
672 | ||
673 | splx(s); | |
674 | return; | |
675 | } | |
676 | ||
677 | static struct nd_defrouter * | |
678 | defrtrlist_update(new) | |
679 | struct nd_defrouter *new; | |
680 | { | |
681 | struct nd_defrouter *dr, *n; | |
682 | #ifdef __NetBSD__ | |
683 | int s = splsoftnet(); | |
684 | #else | |
685 | int s = splnet(); | |
686 | #endif | |
687 | ||
688 | if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) { | |
689 | /* entry exists */ | |
690 | if (new->rtlifetime == 0) { | |
691 | defrtrlist_del(dr); | |
692 | dr = NULL; | |
693 | } else { | |
694 | /* override */ | |
695 | dr->flags = new->flags; /* xxx flag check */ | |
696 | dr->rtlifetime = new->rtlifetime; | |
697 | dr->expire = new->expire; | |
698 | } | |
699 | splx(s); | |
700 | return(dr); | |
701 | } | |
702 | ||
703 | /* entry does not exist */ | |
704 | if (new->rtlifetime == 0) { | |
705 | splx(s); | |
706 | return(NULL); | |
707 | } | |
708 | ||
709 | n = (struct nd_defrouter *)_MALLOC(sizeof(*n), M_IP6NDP, M_NOWAIT); | |
710 | if (n == NULL) { | |
711 | splx(s); | |
712 | return(NULL); | |
713 | } | |
714 | bzero(n, sizeof(*n)); | |
715 | *n = *new; | |
716 | ||
717 | /* | |
718 | * Insert the new router at the end of the Default Router List. | |
719 | * If there is no other router, install it anyway. Otherwise, | |
720 | * just continue to use the current default router. | |
721 | */ | |
722 | TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); | |
723 | if (TAILQ_FIRST(&nd_defrouter) == n) | |
724 | defrouter_select(); | |
725 | splx(s); | |
726 | ||
727 | return(n); | |
728 | } | |
729 | ||
730 | struct nd_pfxrouter * | |
731 | pfxrtr_lookup(pr, dr) | |
732 | struct nd_prefix *pr; | |
733 | struct nd_defrouter *dr; | |
734 | { | |
735 | struct nd_pfxrouter *search; | |
736 | ||
737 | for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) { | |
738 | if (search->router == dr) | |
739 | break; | |
740 | } | |
741 | ||
742 | return(search); | |
743 | } | |
744 | ||
745 | static void | |
746 | pfxrtr_add(pr, dr) | |
747 | struct nd_prefix *pr; | |
748 | struct nd_defrouter *dr; | |
749 | { | |
750 | struct nd_pfxrouter *new; | |
751 | ||
752 | new = (struct nd_pfxrouter *)_MALLOC(sizeof(*new), M_IP6NDP, M_NOWAIT); | |
753 | if (new == NULL) | |
754 | return; | |
755 | bzero(new, sizeof(*new)); | |
756 | new->router = dr; | |
757 | ||
758 | LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); | |
759 | ||
760 | pfxlist_onlink_check(); | |
761 | } | |
762 | ||
763 | static void | |
764 | pfxrtr_del(pfr) | |
765 | struct nd_pfxrouter *pfr; | |
766 | { | |
767 | LIST_REMOVE(pfr, pfr_entry); | |
768 | _FREE(pfr, M_IP6NDP); | |
769 | } | |
770 | ||
771 | struct nd_prefix * | |
772 | prefix_lookup(pr) | |
773 | struct nd_prefix *pr; | |
774 | { | |
775 | struct nd_prefix *search; | |
776 | ||
777 | for (search = nd_prefix.lh_first; search; search = search->ndpr_next) { | |
778 | if (pr->ndpr_ifp == search->ndpr_ifp && | |
779 | pr->ndpr_plen == search->ndpr_plen && | |
780 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, | |
781 | &search->ndpr_prefix.sin6_addr, | |
782 | pr->ndpr_plen) | |
783 | ) { | |
784 | break; | |
785 | } | |
786 | } | |
787 | ||
788 | return(search); | |
789 | } | |
790 | ||
791 | static int | |
792 | prelist_add(pr, dr) | |
793 | struct nd_prefix *pr; | |
794 | struct nd_defrouter *dr; | |
795 | { | |
796 | struct nd_prefix *new; | |
797 | int i, s; | |
798 | ||
799 | new = (struct nd_prefix *)_MALLOC(sizeof(*new), M_IP6NDP, M_NOWAIT); | |
800 | if (new == NULL) | |
801 | return ENOMEM; | |
802 | bzero(new, sizeof(*new)); | |
803 | *new = *pr; | |
804 | ||
805 | /* initilization */ | |
806 | new->ndpr_statef_onlink = pr->ndpr_statef_onlink; | |
807 | LIST_INIT(&new->ndpr_advrtrs); | |
808 | in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); | |
809 | /* make prefix in the canonical form */ | |
810 | for (i = 0; i < 4; i++) | |
811 | new->ndpr_prefix.sin6_addr.s6_addr32[i] &= | |
812 | new->ndpr_mask.s6_addr32[i]; | |
813 | ||
814 | /* xxx ND_OPT_PI_FLAG_ONLINK processing */ | |
815 | ||
816 | #ifdef __NetBSD__ | |
817 | s = splsoftnet(); | |
818 | #else | |
819 | s = splnet(); | |
820 | #endif | |
821 | /* link ndpr_entry to nd_prefix list */ | |
822 | LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); | |
823 | splx(s); | |
824 | ||
825 | if (dr) { | |
826 | pfxrtr_add(new, dr); | |
827 | #if MIP6 | |
828 | if (mip6_get_md_state_hook) { | |
829 | /* | |
830 | * If we are in UNDEFINED state and a router appears, | |
831 | * select that router and change state. | |
832 | * This case takes care of transitions from UNDEFINED | |
833 | * to FOREIGN when the prefix is not known from before. | |
834 | */ | |
835 | if ((*mip6_get_md_state_hook)() == MIP6_MD_UNDEFINED) { | |
836 | if (mip6_select_defrtr_hook) | |
837 | (*mip6_select_defrtr_hook)(); | |
838 | } | |
839 | } | |
840 | #endif /* MIP6 */ | |
841 | } | |
842 | ||
843 | return 0; | |
844 | } | |
845 | ||
846 | void | |
847 | prelist_remove(pr) | |
848 | struct nd_prefix *pr; | |
849 | { | |
850 | struct nd_pfxrouter *pfr, *next; | |
851 | int s; | |
852 | ||
853 | #ifdef __NetBSD__ | |
854 | s = splsoftnet(); | |
855 | #else | |
856 | s = splnet(); | |
857 | #endif | |
858 | /* unlink ndpr_entry from nd_prefix list */ | |
859 | LIST_REMOVE(pr, ndpr_entry); | |
860 | splx(s); | |
861 | ||
862 | /* free list of routers that adversed the prefix */ | |
863 | for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { | |
864 | next = pfr->pfr_next; | |
865 | ||
866 | _FREE(pfr, M_IP6NDP); | |
867 | } | |
868 | _FREE(pr, M_IP6NDP); | |
869 | ||
870 | pfxlist_onlink_check(); | |
871 | } | |
872 | ||
873 | /* | |
874 | * NOTE: We set address lifetime to keep | |
875 | * address lifetime <= prefix lifetime | |
876 | * invariant. This is to simplify on-link determination code. | |
877 | * If onlink determination is udated, this routine may have to be updated too. | |
878 | */ | |
879 | int | |
880 | prelist_update(new, dr, m) | |
881 | struct nd_prefix *new; | |
882 | struct nd_defrouter *dr; /* may be NULL */ | |
883 | struct mbuf *m; | |
884 | { | |
885 | struct in6_ifaddr *ia6 = NULL; | |
886 | struct nd_prefix *pr; | |
887 | #ifdef __NetBSD__ | |
888 | int s = splsoftnet(); | |
889 | #else | |
890 | int s = splnet(); | |
891 | #endif | |
892 | int error = 0; | |
893 | int auth; | |
894 | struct in6_addrlifetime *lt6; | |
895 | u_char onlink; /* Mobile IPv6 */ | |
896 | ||
897 | auth = 0; | |
898 | if (m) { | |
899 | /* | |
900 | * Authenticity for NA consists authentication for | |
901 | * both IP header and IP datagrams, doesn't it ? | |
902 | */ | |
903 | #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) | |
904 | auth = (m->m_flags & M_AUTHIPHDR | |
905 | && m->m_flags & M_AUTHIPDGM) ? 1 : 0; | |
906 | #endif | |
907 | } | |
908 | ||
909 | if ((pr = prefix_lookup(new)) != NULL) { | |
910 | if (pr->ndpr_ifp != new->ndpr_ifp) { | |
911 | error = EADDRNOTAVAIL; | |
912 | goto end; | |
913 | } | |
914 | ||
915 | #if MIP6 | |
916 | if (mip6_get_home_prefix_hook) { | |
917 | /* | |
918 | * The home prefix should be kept away from updates. | |
919 | * XXXYYY Tunneled RA? New Home Prefix? Unless | |
920 | * configured, the code below will be executed. | |
921 | */ | |
922 | if (pr == (*mip6_get_home_prefix_hook)()) | |
923 | goto noautoconf1; | |
924 | } | |
925 | #endif /* MIP6 */ | |
926 | ||
927 | /* update prefix information */ | |
928 | pr->ndpr_flags = new->ndpr_flags; | |
929 | pr->ndpr_vltime = new->ndpr_vltime; | |
930 | pr->ndpr_pltime = new->ndpr_pltime; | |
931 | pr->ndpr_preferred = new->ndpr_preferred; | |
932 | pr->ndpr_expire = new->ndpr_expire; | |
933 | ||
934 | /* | |
935 | * RFC 2462 5.5.3 (d) or (e) | |
936 | * We got a prefix which we have seen in the past. | |
937 | */ | |
938 | if (!new->ndpr_raf_auto) | |
939 | goto noautoconf1; | |
940 | ||
941 | if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) | |
942 | ia6 = NULL; | |
943 | else | |
944 | ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); | |
945 | ||
946 | if (ia6 == NULL) { | |
947 | /* | |
948 | * Special case: | |
949 | * (1) We have seen the prefix advertised before, but | |
950 | * we have never performed autoconfig for this prefix. | |
951 | * This is because Autonomous bit was 0 previously, or | |
952 | * autoconfig failed due to some other reasons. | |
953 | * (2) We have seen the prefix advertised before and | |
954 | * we have performed autoconfig in the past, but | |
955 | * we seem to have no interface address right now. | |
956 | * This is because the interface address have expired. | |
957 | * | |
958 | * This prefix is fresh, with respect to autoconfig | |
959 | * process. | |
960 | * | |
961 | * Add an address based on RFC 2462 5.5.3 (d). | |
962 | */ | |
963 | ia6 = in6_ifadd(pr->ndpr_ifp, | |
964 | &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr, | |
965 | new->ndpr_plen); | |
966 | if (!ia6) { | |
967 | error = EADDRNOTAVAIL; | |
968 | log(LOG_ERR, "prelist_update: failed to add a " | |
969 | "new address\n"); | |
970 | goto noautoconf1; | |
971 | } | |
972 | ||
973 | lt6 = &ia6->ia6_lifetime; | |
974 | ||
975 | /* address lifetime <= prefix lifetime */ | |
976 | lt6->ia6t_vltime = new->ndpr_vltime; | |
977 | lt6->ia6t_pltime = new->ndpr_pltime; | |
978 | in6_init_address_ltimes(new, lt6, 1); | |
979 | } else { | |
980 | #define TWOHOUR (120*60) | |
981 | /* | |
982 | * We have seen the prefix before, and we have added | |
983 | * interface address in the past. We still have | |
984 | * the interface address assigned. | |
985 | * | |
986 | * update address lifetime based on RFC 2462 | |
987 | * 5.5.3 (e). | |
988 | */ | |
989 | int update = 0; | |
990 | ||
991 | lt6 = &ia6->ia6_lifetime; | |
992 | ||
993 | #if 0 /* RFC 2462 5.5.3 (e) */ | |
994 | lt6->ia6t_pltime = new->ndpr_pltime; | |
995 | if (TWOHOUR < new->ndpr_vltime | |
996 | || lt6pr->nd < new->ndpr_vltime) { | |
997 | lt6->ia6t_vltime = new->ndpr_vltime; | |
998 | update++; | |
999 | } else if (auth | |
1000 | && lt6->ia6t_vltime <= TWOHOUR0 | |
1001 | && new->ndpr_vltime <= lt6->ia6t_vltime) { | |
1002 | lt6->ia6t_vltime = new->ndpr_vltime; | |
1003 | update++; | |
1004 | } else { | |
1005 | lt6->ia6t_vltime = TWOHOUR; | |
1006 | update++; | |
1007 | } | |
1008 | ||
1009 | /* 2 hour rule is not imposed for pref lifetime */ | |
1010 | new->ndpr_apltime = new->ndpr_pltime; | |
1011 | lt6->ia6t_pltime = new->ndpr_pltime; | |
1012 | #else /* update from Jim Bound, (ipng 6712) */ | |
1013 | if (TWOHOUR < new->ndpr_vltime | |
1014 | || lt6->ia6t_vltime < new->ndpr_vltime) { | |
1015 | lt6->ia6t_vltime = new->ndpr_vltime; | |
1016 | update++; | |
1017 | } else if (auth) { | |
1018 | lt6->ia6t_vltime = new->ndpr_vltime; | |
1019 | update++; | |
1020 | } | |
1021 | ||
1022 | /* jim bound rule is not imposed for pref lifetime */ | |
1023 | lt6->ia6t_pltime = new->ndpr_pltime; | |
1024 | #endif | |
1025 | in6_init_address_ltimes(new, lt6, update); | |
1026 | } | |
1027 | ||
1028 | noautoconf1: | |
1029 | ||
1030 | #if 0 | |
1031 | /* address lifetime expire processing, RFC 2462 5.5.4. */ | |
1032 | if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) { | |
1033 | struct in6_ifaddr *ia6; | |
1034 | ||
1035 | ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); | |
1036 | if (ia6) | |
1037 | ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; | |
1038 | } | |
1039 | #endif | |
1040 | ||
1041 | onlink = pr->ndpr_statef_onlink; /* Mobile IPv6 */ | |
1042 | ||
1043 | if (dr && pfxrtr_lookup(pr, dr) == NULL) | |
1044 | pfxrtr_add(pr, dr); | |
1045 | ||
1046 | #if MIP6 | |
1047 | if (mip6_prelist_update_hook) { | |
1048 | /* | |
1049 | * Check for home prefix. It can't be a fresh prefix | |
1050 | * (since it's static), so check here. | |
1051 | */ | |
1052 | (*mip6_prelist_update_hook)(pr, dr); | |
1053 | } | |
1054 | ||
1055 | if (mip6_probe_pfxrtrs_hook) { | |
1056 | /* | |
1057 | * If this prefix previously was detached, maybe we | |
1058 | * have moved. | |
1059 | */ | |
1060 | if (!onlink) | |
1061 | (*mip6_probe_pfxrtrs_hook)(); | |
1062 | } | |
1063 | #endif /* MIP6 */ | |
1064 | ||
1065 | } else { | |
1066 | int error_tmp; | |
1067 | ||
1068 | if (new->ndpr_vltime == 0) goto end; | |
1069 | ||
1070 | bzero(&new->ndpr_addr, sizeof(struct in6_addr)); | |
1071 | ||
1072 | /* | |
1073 | * RFC 2462 5.5.3 (d) | |
1074 | * We got a fresh prefix. Perform some sanity checks | |
1075 | * and add an interface address by appending interface ID | |
1076 | * to the advertised prefix. | |
1077 | */ | |
1078 | if (!new->ndpr_raf_auto) | |
1079 | goto noautoconf2; | |
1080 | ||
1081 | ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr, | |
1082 | &new->ndpr_addr, new->ndpr_plen); | |
1083 | if (!ia6) { | |
1084 | error = EADDRNOTAVAIL; | |
1085 | log(LOG_ERR, "prelist_update: " | |
1086 | "failed to add a new address\n"); | |
1087 | goto noautoconf2; | |
1088 | } | |
1089 | /* set onlink bit if an interface route is configured */ | |
1090 | new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0; | |
1091 | ||
1092 | lt6 = &ia6->ia6_lifetime; | |
1093 | ||
1094 | /* address lifetime <= prefix lifetime */ | |
1095 | lt6->ia6t_vltime = new->ndpr_vltime; | |
1096 | lt6->ia6t_pltime = new->ndpr_pltime; | |
1097 | in6_init_address_ltimes(new, lt6, 1); | |
1098 | ||
1099 | noautoconf2: | |
1100 | error_tmp = prelist_add(new, dr); | |
1101 | error = error_tmp ? error_tmp : error; | |
1102 | ||
1103 | #if MIP6 | |
1104 | if (mip6_probe_pfxrtrs_hook) { | |
1105 | /* This is a new prefix, maybe we have moved. */ | |
1106 | (*mip6_probe_pfxrtrs_hook)(); | |
1107 | } | |
1108 | ||
1109 | if (mip6_minus_a_case_hook) { | |
1110 | /* | |
1111 | * If we are still looking for an autoconfigured home | |
1112 | * address when we are in "minus a" case, here's a new | |
1113 | * prefix and hopefully we can use the address derived | |
1114 | *from that. | |
1115 | */ | |
1116 | if (ia6) | |
1117 | (*mip6_minus_a_case_hook)(new); | |
1118 | } | |
1119 | #endif /* MIP6 */ | |
1120 | ||
1121 | } | |
1122 | ||
1123 | end: | |
1124 | splx(s); | |
1125 | return error; | |
1126 | } | |
1127 | ||
1128 | /* | |
1129 | * A supplement function used in the on-link detection below; | |
1130 | * detect if a given prefix has a (probably) reachable advertising router. | |
1131 | * XXX: lengthy function name... | |
1132 | */ | |
1133 | struct nd_pfxrouter * | |
1134 | find_pfxlist_reachable_router(pr) | |
1135 | struct nd_prefix *pr; | |
1136 | { | |
1137 | struct nd_pfxrouter *pfxrtr; | |
1138 | struct rtentry *rt; | |
1139 | struct llinfo_nd6 *ln; | |
1140 | ||
1141 | for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr; | |
1142 | pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) { | |
1143 | if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0, | |
1144 | pfxrtr->router->ifp)) && | |
1145 | (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && | |
1146 | ND6_IS_LLINFO_PROBREACH(ln)) | |
1147 | break; /* found */ | |
1148 | } | |
1149 | ||
1150 | return(pfxrtr); | |
1151 | ||
1152 | } | |
1153 | ||
1154 | /* | |
1155 | * Check if each prefix in the prefix list has at least one available router | |
1156 | * that advertised the prefix (A router is "available" if its neighbor cache | |
1157 | * entry has reachable or probably reachable). | |
1158 | * If the check fails, the prefix may be off-link, because, for example, | |
1159 | * we have moved from the network but the lifetime of the prefix has not | |
1160 | * been expired yet. So we should not use the prefix if there is another | |
1161 | * prefix that has an available router. | |
1162 | * But if there is no prefix that has an available router, we still regards | |
1163 | * all the prefixes as on-link. This is because we can't tell if all the | |
1164 | * routers are simply dead or if we really moved from the network and there | |
1165 | * is no router around us. | |
1166 | */ | |
1167 | void | |
1168 | pfxlist_onlink_check() | |
1169 | { | |
1170 | struct nd_prefix *pr; | |
1171 | ||
1172 | /* | |
1173 | * Check if there is a prefix that has a reachable advertising | |
1174 | * router. | |
1175 | */ | |
1176 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
1177 | if (find_pfxlist_reachable_router(pr)) | |
1178 | break; | |
1179 | } | |
1180 | ||
1181 | if (pr) { | |
1182 | /* | |
1183 | * There is at least one prefix that has a reachable router. | |
1184 | * First, detach prefixes which has no reachable advertising | |
1185 | * router and then attach other prefixes. | |
1186 | * The order is important since an attached prefix and a | |
1187 | * detached prefix may have a same interface route. | |
1188 | */ | |
1189 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
1190 | if (find_pfxlist_reachable_router(pr) == NULL && | |
1191 | pr->ndpr_statef_onlink) { | |
1192 | pr->ndpr_statef_onlink = 0; | |
1193 | nd6_detach_prefix(pr); | |
1194 | } | |
1195 | } | |
1196 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { | |
1197 | if (find_pfxlist_reachable_router(pr) && | |
1198 | pr->ndpr_statef_onlink == 0) | |
1199 | nd6_attach_prefix(pr); | |
1200 | } | |
1201 | } | |
1202 | else { | |
1203 | /* there is no prefix that has a reachable router */ | |
1204 | for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) | |
1205 | if (pr->ndpr_statef_onlink == 0) | |
1206 | nd6_attach_prefix(pr); | |
1207 | } | |
1208 | } | |
1209 | ||
1210 | static void | |
1211 | nd6_detach_prefix(pr) | |
1212 | struct nd_prefix *pr; | |
1213 | { | |
1214 | struct in6_ifaddr *ia6; | |
1215 | struct sockaddr_in6 sa6, mask6; | |
1216 | ||
1217 | /* | |
1218 | * Delete the interface route associated with the prefix. | |
1219 | */ | |
1220 | bzero(&sa6, sizeof(sa6)); | |
1221 | sa6.sin6_family = AF_INET6; | |
1222 | sa6.sin6_len = sizeof(sa6); | |
1223 | bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, | |
1224 | sizeof(struct in6_addr)); | |
1225 | bzero(&mask6, sizeof(mask6)); | |
1226 | mask6.sin6_family = AF_INET6; | |
1227 | mask6.sin6_len = sizeof(sa6); | |
1228 | bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); | |
1229 | { | |
1230 | int e; | |
1231 | ||
1232 | e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, | |
1233 | (struct sockaddr *)&mask6, 0, NULL); | |
1234 | if (e) { | |
1235 | log(LOG_ERR, | |
1236 | "nd6_detach_prefix: failed to delete route: " | |
1237 | "%s/%d (errno = %d)\n", | |
1238 | ip6_sprintf(&sa6.sin6_addr), | |
1239 | pr->ndpr_plen, | |
1240 | e); | |
1241 | } | |
1242 | } | |
1243 | ||
1244 | /* | |
1245 | * Mark the address derived from the prefix detached so that | |
1246 | * it won't be used as a source address for a new connection. | |
1247 | */ | |
1248 | if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) | |
1249 | ia6 = NULL; | |
1250 | else | |
1251 | ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); | |
1252 | if (ia6) | |
1253 | #if MIP6 | |
1254 | if (mip6_get_home_prefix_hook) | |
1255 | if (pr != (*mip6_get_home_prefix_hook)()) | |
1256 | ia6->ia6_flags |= IN6_IFF_DETACHED; | |
1257 | #else | |
1258 | ia6->ia6_flags |= IN6_IFF_DETACHED; | |
1259 | #endif | |
1260 | } | |
1261 | ||
1262 | static void | |
1263 | nd6_attach_prefix(pr) | |
1264 | struct nd_prefix *pr; | |
1265 | { | |
1266 | struct ifaddr *ifa; | |
1267 | struct in6_ifaddr *ia6; | |
1268 | ||
1269 | /* | |
1270 | * Add the interface route associated with the prefix(if necessary) | |
1271 | * Should we consider if the L bit is set in pr->ndpr_flags? | |
1272 | */ | |
1273 | ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix, | |
1274 | pr->ndpr_ifp); | |
1275 | if (ifa == NULL) { | |
1276 | log(LOG_ERR, | |
1277 | "nd6_attach_prefix: failed to find any ifaddr" | |
1278 | " to add route for a prefix(%s/%d)\n", | |
1279 | ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen); | |
1280 | } | |
1281 | else { | |
1282 | int e; | |
1283 | struct sockaddr_in6 mask6; | |
1284 | ||
1285 | bzero(&mask6, sizeof(mask6)); | |
1286 | mask6.sin6_family = AF_INET6; | |
1287 | mask6.sin6_len = sizeof(mask6); | |
1288 | mask6.sin6_addr = pr->ndpr_mask; | |
1289 | e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, | |
1290 | ifa->ifa_addr, (struct sockaddr *)&mask6, | |
1291 | ifa->ifa_flags, NULL); | |
1292 | if (e == 0) | |
1293 | pr->ndpr_statef_onlink = 1; | |
1294 | else { | |
1295 | log(LOG_ERR, | |
1296 | "nd6_attach_prefix: failed to add route for" | |
1297 | " a prefix(%s/%d), errno = %d\n", | |
1298 | ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e); | |
1299 | } | |
1300 | } | |
1301 | ||
1302 | /* | |
1303 | * Now the address derived from the prefix can be used as a source | |
1304 | * for a new connection, so clear the detached flag. | |
1305 | */ | |
1306 | if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) | |
1307 | ia6 = NULL; | |
1308 | else | |
1309 | ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); | |
1310 | if (ia6) { | |
1311 | ia6->ia6_flags &= ~IN6_IFF_DETACHED; | |
1312 | if (pr->ndpr_statef_onlink) | |
1313 | ia6->ia_flags |= IFA_ROUTE; | |
1314 | } | |
1315 | } | |
1316 | ||
1317 | static struct in6_ifaddr * | |
1318 | in6_ifadd(ifp, in6, addr, prefixlen) | |
1319 | struct ifnet *ifp; | |
1320 | struct in6_addr *in6; | |
1321 | struct in6_addr *addr; | |
1322 | int prefixlen; /* prefix len of the new prefix in "in6" */ | |
1323 | { | |
1324 | struct ifaddr *ifa; | |
1325 | struct in6_ifaddr *ia, *ib, *oia; | |
1326 | int s, error; | |
1327 | struct in6_addr mask; | |
1328 | ||
1329 | in6_len2mask(&mask, prefixlen); | |
1330 | ||
1331 | /* find link-local address (will be interface ID) */ | |
1332 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ | |
1333 | if (ifa) | |
1334 | ib = (struct in6_ifaddr *)ifa; | |
1335 | else | |
1336 | return NULL; | |
1337 | ||
1338 | #if 0 /* don't care link local addr state, and always do DAD */ | |
1339 | /* if link-local address is not eligible, do not autoconfigure. */ | |
1340 | if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) { | |
1341 | printf("in6_ifadd: link-local address not ready\n"); | |
1342 | return NULL; | |
1343 | } | |
1344 | #endif | |
1345 | ||
1346 | /* prefixlen + ifidlen must be equal to 128 */ | |
1347 | if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) { | |
1348 | log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s" | |
1349 | "(prefix=%d ifid=%d)\n", if_name(ifp), | |
1350 | prefixlen, | |
1351 | 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr)); | |
1352 | return NULL; | |
1353 | } | |
1354 | ||
1355 | /* make ifaddr */ | |
0b4e3aa0 | 1356 | ia = (struct in6_ifaddr *)_MALLOC(sizeof(*ia), M_IFADDR, M_NOWAIT); |
1c79356b A |
1357 | if (ia == NULL) { |
1358 | printf("ENOBUFS in in6_ifadd %d\n", __LINE__); | |
1359 | return NULL; | |
1360 | } | |
1361 | ||
1362 | bzero((caddr_t)ia, sizeof(*ia)); | |
1363 | ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; | |
1364 | if (ifp->if_flags & IFF_POINTOPOINT) | |
1365 | ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; | |
1366 | else | |
1367 | ia->ia_ifa.ifa_dstaddr = NULL; | |
1368 | ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; | |
1369 | ia->ia_ifp = ifp; | |
1370 | ||
1371 | /* link to in6_ifaddr */ | |
1372 | if ((oia = in6_ifaddr) != NULL) { | |
1373 | for( ; oia->ia_next; oia = oia->ia_next) | |
1374 | continue; | |
1375 | oia->ia_next = ia; | |
1376 | } else | |
1377 | in6_ifaddr = ia; | |
1378 | ia->ia_ifa.ifa_refcnt++; | |
1379 | ||
1380 | /* link to if_addrlist */ | |
1381 | #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) | |
1382 | if ((ifa = ifp->if_addrlist) != NULL) { | |
1383 | for ( ; ifa->ifa_next; ifa = ifa->ifa_next) | |
1384 | continue; | |
1385 | ifa->ifa_next = (struct ifaddr *)ia; | |
1386 | } | |
1387 | #else | |
1388 | if (ifp->if_addrlist.tqh_first != NULL) { | |
1389 | TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, | |
1390 | ifa_list); | |
1391 | ia->ia_ifa.ifa_refcnt++; | |
1392 | } | |
1393 | #endif | |
1394 | #if 0 | |
1395 | else { | |
1396 | /* | |
1397 | * this should not be the case because there is at least one | |
1398 | * link-local address(see the beginning of the function). | |
1399 | */ | |
1400 | TAILQ_INIT(&ifp->if_addrlist); | |
1401 | } | |
1402 | #endif | |
1403 | ||
1404 | /* new address */ | |
1405 | ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); | |
1406 | ia->ia_addr.sin6_family = AF_INET6; | |
1407 | /* prefix */ | |
1408 | bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)); | |
1409 | ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; | |
1410 | ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; | |
1411 | ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; | |
1412 | ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; | |
1413 | /* interface ID */ | |
1414 | ia->ia_addr.sin6_addr.s6_addr32[0] | |
1415 | |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); | |
1416 | ia->ia_addr.sin6_addr.s6_addr32[1] | |
1417 | |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); | |
1418 | ia->ia_addr.sin6_addr.s6_addr32[2] | |
1419 | |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); | |
1420 | ia->ia_addr.sin6_addr.s6_addr32[3] | |
1421 | |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); | |
1422 | ||
1423 | /* new prefix */ | |
1424 | ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); | |
1425 | ia->ia_prefixmask.sin6_family = AF_INET6; | |
1426 | bcopy(&mask, &ia->ia_prefixmask.sin6_addr, | |
1427 | sizeof(ia->ia_prefixmask.sin6_addr)); | |
1428 | ||
1429 | /* same routine */ | |
1430 | ia->ia_ifa.ifa_rtrequest = | |
1431 | (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest; | |
1432 | ia->ia_ifa.ifa_flags |= RTF_CLONING; | |
1433 | ia->ia_ifa.ifa_metric = ifp->if_metric; | |
1434 | ||
1435 | /* add interface route */ | |
1436 | if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) { | |
1437 | log(LOG_NOTICE, "in6_ifadd: failed to add an interface route " | |
1438 | "for %s/%d on %s, errno = %d\n", | |
1439 | ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen, | |
1440 | if_name(ifp), error); | |
1441 | } | |
1442 | else | |
1443 | ia->ia_flags |= IFA_ROUTE; | |
1444 | ||
1445 | *addr = ia->ia_addr.sin6_addr; | |
1446 | ||
1447 | if (ifp->if_flags & IFF_MULTICAST) { | |
1448 | int error; /* not used */ | |
1449 | struct in6_addr sol6; | |
1450 | ||
1451 | #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__) | |
1452 | /* Restore saved multicast addresses(if any). */ | |
1453 | in6_restoremkludge(ia, ifp); | |
1454 | #endif | |
1455 | ||
1456 | /* join solicited node multicast address */ | |
1457 | bzero(&sol6, sizeof(sol6)); | |
1458 | sol6.s6_addr16[0] = htons(0xff02); | |
1459 | sol6.s6_addr16[1] = htons(ifp->if_index); | |
1460 | sol6.s6_addr32[1] = 0; | |
1461 | sol6.s6_addr32[2] = htonl(1); | |
1462 | sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; | |
1463 | sol6.s6_addr8[12] = 0xff; | |
1464 | (void)in6_addmulti(&sol6, ifp, &error); | |
1465 | } | |
1466 | ||
1467 | ia->ia6_flags |= IN6_IFF_TENTATIVE; | |
1468 | ||
1469 | /* | |
1470 | * To make the interface up. Only AF_INET6 in ia is used... | |
1471 | */ | |
1472 | s = splimp(); | |
1473 | error = dlil_ioctl(PF_INET6, ifp, SIOCSIFADDR, (caddr_t)ia); | |
1474 | if (error == EOPNOTSUPP) | |
1475 | error = 0; | |
1476 | if (error) { | |
1477 | ||
1478 | splx(s); | |
1479 | return NULL; | |
1480 | } | |
1481 | splx(s); | |
1482 | ||
1483 | /* Perform DAD, if needed. */ | |
1484 | nd6_dad_start((struct ifaddr *)ia, NULL); | |
1485 | ||
1486 | return ia; | |
1487 | } | |
1488 | ||
1489 | int | |
1490 | in6_ifdel(ifp, in6) | |
1491 | struct ifnet *ifp; | |
1492 | struct in6_addr *in6; | |
1493 | { | |
1494 | #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) | |
1495 | struct ifaddr *ifa; | |
1496 | #endif | |
1497 | struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL; | |
1498 | struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL; | |
1499 | ||
1500 | if (!ifp) | |
1501 | return -1; | |
1502 | ||
1503 | ia = in6ifa_ifpwithaddr(ifp, in6); | |
1504 | if (!ia) | |
1505 | return -1; | |
1506 | ||
1507 | if (ifp->if_flags & IFF_MULTICAST) { | |
1508 | /* | |
1509 | * delete solicited multicast addr for deleting host id | |
1510 | */ | |
1511 | struct in6_multi *in6m; | |
1512 | struct in6_addr llsol; | |
1513 | bzero(&llsol, sizeof(struct in6_addr)); | |
1514 | llsol.s6_addr16[0] = htons(0xff02); | |
1515 | llsol.s6_addr16[1] = htons(ifp->if_index); | |
1516 | llsol.s6_addr32[1] = 0; | |
1517 | llsol.s6_addr32[2] = htonl(1); | |
1518 | llsol.s6_addr32[3] = | |
1519 | ia->ia_addr.sin6_addr.s6_addr32[3]; | |
1520 | llsol.s6_addr8[12] = 0xff; | |
1521 | ||
1522 | IN6_LOOKUP_MULTI(llsol, ifp, in6m); | |
1523 | if (in6m) | |
1524 | in6_delmulti(in6m); | |
1525 | } | |
1526 | ||
1527 | if (ia->ia_flags & IFA_ROUTE) { | |
1528 | rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); | |
1529 | ia->ia_flags &= ~IFA_ROUTE; | |
1530 | } | |
1531 | ||
1532 | #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) | |
1533 | if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia) { | |
1534 | ifp->if_addrlist = ifa->ifa_next; | |
1535 | } else { | |
1536 | while (ifa->ifa_next && | |
1537 | (ifa->ifa_next != (struct ifaddr *)ia)) | |
1538 | ifa = ifa->ifa_next; | |
1539 | if (ifa->ifa_next) | |
1540 | ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next; | |
1541 | else | |
1542 | return -1; | |
1543 | } | |
1544 | #else | |
1545 | TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); | |
1546 | #endif | |
1547 | IFAFREE(&ia->ia_ifa); | |
1548 | ||
1549 | /* lladdr is never deleted */ | |
1550 | oia = ia; | |
1551 | if (oia == (ia = in6_ifaddr)) | |
1552 | in6_ifaddr = ia->ia_next; | |
1553 | else { | |
1554 | while (ia->ia_next && (ia->ia_next != oia)) | |
1555 | ia = ia->ia_next; | |
1556 | if (ia->ia_next) | |
1557 | ia->ia_next = oia->ia_next; | |
1558 | else | |
1559 | return -1; | |
1560 | } | |
1561 | ||
1562 | #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__) | |
1563 | in6_savemkludge(oia); | |
1564 | #endif | |
1565 | IFAFREE((&oia->ia_ifa)); | |
1566 | /* xxx | |
1567 | rtrequest(RTM_DELETE, | |
1568 | (struct sockaddr *)&ia->ia_addr, | |
1569 | (struct sockaddr *)0 | |
1570 | (struct sockaddr *)&ia->ia_prefixmask, | |
1571 | RTF_UP|RTF_CLONING, | |
1572 | (struct rtentry **)0); | |
1573 | */ | |
1574 | return 0; | |
1575 | } | |
1576 | ||
1577 | int | |
1578 | in6_init_prefix_ltimes(struct nd_prefix *ndpr) | |
1579 | { | |
1580 | #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__) | |
1581 | long time_second = time.tv_sec; | |
1582 | #endif | |
1583 | ||
1584 | /* check if preferred lifetime > valid lifetime */ | |
1585 | if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { | |
1586 | log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" | |
1587 | "(%d) is greater than valid lifetime(%d)\n", | |
1588 | (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime); | |
1589 | return (EINVAL); | |
1590 | } | |
1591 | if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) | |
1592 | ndpr->ndpr_preferred = 0; | |
1593 | else | |
1594 | ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime; | |
1595 | if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) | |
1596 | ndpr->ndpr_expire = 0; | |
1597 | else | |
1598 | ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime; | |
1599 | ||
1600 | return 0; | |
1601 | } | |
1602 | ||
1603 | static void | |
1604 | in6_init_address_ltimes(struct nd_prefix *new, | |
1605 | struct in6_addrlifetime *lt6, | |
1606 | int update_vltime) | |
1607 | { | |
1608 | #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined (__APPLE__) | |
1609 | long time_second = time.tv_sec; | |
1610 | #endif | |
1611 | ||
1612 | /* Valid lifetime must not be updated unless explicitly specified. */ | |
1613 | if (update_vltime) { | |
1614 | /* init ia6t_expire */ | |
1615 | if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) | |
1616 | lt6->ia6t_expire = 0; | |
1617 | else { | |
1618 | lt6->ia6t_expire = time_second; | |
1619 | lt6->ia6t_expire += lt6->ia6t_vltime; | |
1620 | } | |
1621 | /* Ensure addr lifetime <= prefix lifetime. */ | |
1622 | if (new->ndpr_expire && lt6->ia6t_expire && | |
1623 | new->ndpr_expire < lt6->ia6t_expire) | |
1624 | lt6->ia6t_expire = new->ndpr_expire; | |
1625 | } | |
1626 | ||
1627 | /* init ia6t_preferred */ | |
1628 | if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) | |
1629 | lt6->ia6t_preferred = 0; | |
1630 | else { | |
1631 | lt6->ia6t_preferred = time_second; | |
1632 | lt6->ia6t_preferred += lt6->ia6t_pltime; | |
1633 | } | |
1634 | /* Ensure addr lifetime <= prefix lifetime. */ | |
1635 | if (new->ndpr_preferred && lt6->ia6t_preferred | |
1636 | && new->ndpr_preferred < lt6->ia6t_preferred) | |
1637 | lt6->ia6t_preferred = new->ndpr_preferred; | |
1638 | } | |
1639 | ||
1640 | /* | |
1641 | * Delete all the routing table entries that use the specified gateway. | |
1642 | * XXX: this function causes search through all entries of routing table, so | |
1643 | * it shouldn't be called when acting as a router. | |
1644 | */ | |
1645 | void | |
1646 | rt6_flush(gateway, ifp) | |
1647 | struct in6_addr *gateway; | |
1648 | struct ifnet *ifp; | |
1649 | { | |
1650 | struct radix_node_head *rnh = rt_tables[AF_INET6]; | |
1651 | #ifdef __NetBSD__ | |
1652 | int s = splsoftnet(); | |
1653 | #else | |
1654 | int s = splnet(); | |
1655 | #endif | |
1656 | ||
1657 | /* We'll care only link-local addresses */ | |
1658 | if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { | |
1659 | splx(s); | |
1660 | return; | |
1661 | } | |
1662 | /* XXX: hack for KAME's link-local address kludge */ | |
1663 | gateway->s6_addr16[1] = htons(ifp->if_index); | |
1664 | ||
1665 | rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); | |
1666 | splx(s); | |
1667 | } | |
1668 | ||
1669 | static int | |
1670 | rt6_deleteroute(rn, arg) | |
1671 | struct radix_node *rn; | |
1672 | void *arg; | |
1673 | { | |
1674 | #define SIN6(s) ((struct sockaddr_in6 *)s) | |
1675 | struct rtentry *rt = (struct rtentry *)rn; | |
1676 | struct in6_addr *gate = (struct in6_addr *)arg; | |
1677 | ||
1678 | if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) | |
1679 | return(0); | |
1680 | ||
1681 | if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr)) | |
1682 | return(0); | |
1683 | ||
1684 | /* | |
1685 | * We delete only host route. This means, in particular, we don't | |
1686 | * delete default route. | |
1687 | */ | |
1688 | if ((rt->rt_flags & RTF_HOST) == 0) | |
1689 | return(0); | |
1690 | ||
1691 | return(rtrequest(RTM_DELETE, rt_key(rt), | |
1692 | rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0)); | |
1693 | #undef SIN6 | |
1694 | } | |
1695 | ||
1696 | int | |
1697 | nd6_setdefaultiface(ifindex) | |
1698 | int ifindex; | |
1699 | { | |
1700 | int error = 0; | |
1701 | ||
1702 | if (ifindex < 0 || if_index < ifindex) | |
1703 | return(EINVAL); | |
1704 | ||
1705 | if (nd6_defifindex != ifindex) { | |
1706 | nd6_defifindex = ifindex; | |
1707 | if (nd6_defifindex > 0) | |
1708 | nd6_defifp = ifindex2ifnet[nd6_defifindex]; | |
1709 | else | |
1710 | nd6_defifp = NULL; | |
1711 | ||
1712 | /* | |
1713 | * If the Default Router List is empty, install a route | |
1714 | * to the specified interface as default or remove the default | |
1715 | * route when the default interface becomes canceled. | |
1716 | * The check for the queue is actually redundant, but | |
1717 | * we do this here to avoid re-install the default route | |
1718 | * if the list is NOT empty. | |
1719 | */ | |
1720 | if (TAILQ_FIRST(&nd_defrouter) == NULL) | |
1721 | defrouter_select(); | |
1722 | } | |
1723 | ||
1724 | return(error); | |
1725 | } |