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