1 | /* $NetBSD: nd6_rtr.c,v 1.120 2016/11/15 01:50:06 ozaki-r Exp $ */ |
2 | /* $KAME: nd6_rtr.c,v 1.95 2001/02/07 08:09:47 itojun Exp $ */ |
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 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: nd6_rtr.c,v 1.120 2016/11/15 01:50:06 ozaki-r Exp $" ); |
35 | |
36 | #include <sys/param.h> |
37 | #include <sys/systm.h> |
38 | #include <sys/malloc.h> |
39 | #include <sys/mbuf.h> |
40 | #include <sys/socket.h> |
41 | #include <sys/sockio.h> |
42 | #include <sys/time.h> |
43 | #include <sys/kernel.h> |
44 | #include <sys/errno.h> |
45 | #include <sys/ioctl.h> |
46 | #include <sys/syslog.h> |
47 | #include <sys/cprng.h> |
48 | |
49 | #include <net/if.h> |
50 | #include <net/if_types.h> |
51 | #include <net/if_dl.h> |
52 | |
53 | #include <netinet/in.h> |
54 | #include <netinet6/in6_var.h> |
55 | #include <netinet6/in6_ifattach.h> |
56 | #include <netinet/ip6.h> |
57 | #include <netinet6/ip6_var.h> |
58 | #include <netinet6/nd6.h> |
59 | #include <netinet/icmp6.h> |
60 | #include <netinet6/icmp6_private.h> |
61 | #include <netinet6/scope6_var.h> |
62 | |
63 | #include <net/net_osdep.h> |
64 | |
65 | static int rtpref(struct nd_defrouter *); |
66 | static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); |
67 | static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *, |
68 | struct mbuf *, int); |
69 | static struct in6_ifaddr *in6_ifadd(struct nd_prefixctl *, int, struct psref *); |
70 | static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *, |
71 | struct nd_defrouter *); |
72 | static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *); |
73 | static void pfxrtr_del(struct nd_pfxrouter *); |
74 | static struct nd_pfxrouter *find_pfxlist_reachable_router |
75 | (struct nd_prefix *); |
76 | static void defrouter_delreq(struct nd_defrouter *); |
77 | |
78 | static int in6_init_prefix_ltimes(struct nd_prefix *); |
79 | static void in6_init_address_ltimes(struct nd_prefix *, |
80 | struct in6_addrlifetime *); |
81 | static void purge_detached(struct ifnet *); |
82 | |
83 | static int rt6_deleteroute_matcher(struct rtentry *, void *); |
84 | |
85 | extern int nd6_recalc_reachtm_interval; |
86 | |
87 | static struct ifnet *nd6_defifp; |
88 | int nd6_defifindex; |
89 | |
90 | int ip6_use_tempaddr = 0; |
91 | |
92 | int ip6_desync_factor; |
93 | u_int32_t ip6_temp_preferred_lifetime = DEF_TEMP_PREFERRED_LIFETIME; |
94 | u_int32_t ip6_temp_valid_lifetime = DEF_TEMP_VALID_LIFETIME; |
95 | int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE; |
96 | |
97 | int nd6_numroutes = 0; |
98 | |
99 | /* RTPREF_MEDIUM has to be 0! */ |
100 | #define RTPREF_HIGH 1 |
101 | #define RTPREF_MEDIUM 0 |
102 | #define RTPREF_LOW (-1) |
103 | #define RTPREF_RESERVED (-2) |
104 | #define RTPREF_INVALID (-3) /* internal */ |
105 | |
106 | static inline bool |
107 | nd6_is_llinfo_probreach(struct nd_defrouter *dr) |
108 | { |
109 | struct llentry *ln = NULL; |
110 | |
111 | ln = nd6_lookup(&dr->rtaddr, dr->ifp, false); |
112 | if (ln == NULL) |
113 | return false; |
114 | |
115 | if (!ND6_IS_LLINFO_PROBREACH(ln)) { |
116 | LLE_RUNLOCK(ln); |
117 | return false; |
118 | } |
119 | |
120 | LLE_RUNLOCK(ln); |
121 | return true; |
122 | } |
123 | |
124 | /* |
125 | * Receive Router Solicitation Message - just for routers. |
126 | * Router solicitation/advertisement is mostly managed by a userland program |
127 | * (rtadvd) so here we have no function like nd6_ra_output(). |
128 | * |
129 | * Based on RFC 2461 |
130 | */ |
131 | void |
132 | nd6_rs_input(struct mbuf *m, int off, int icmp6len) |
133 | { |
134 | struct ifnet *ifp; |
135 | struct nd_ifinfo *ndi; |
136 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
137 | struct nd_router_solicit *nd_rs; |
138 | struct in6_addr saddr6 = ip6->ip6_src; |
139 | char *lladdr = NULL; |
140 | int lladdrlen = 0; |
141 | union nd_opts ndopts; |
142 | struct psref psref; |
143 | |
144 | ifp = m_get_rcvif_psref(m, &psref); |
145 | if (ifp == NULL) |
146 | goto freeit; |
147 | |
148 | ndi = ND_IFINFO(ifp); |
149 | |
150 | /* If I'm not a router, ignore it. */ |
151 | if (nd6_accepts_rtadv(ndi) || !ip6_forwarding) |
152 | goto freeit; |
153 | |
154 | /* Sanity checks */ |
155 | if (ip6->ip6_hlim != 255) { |
156 | nd6log(LOG_ERR, "invalid hlim (%d) from %s to %s on %s\n" , |
157 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), |
158 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp)); |
159 | goto bad; |
160 | } |
161 | |
162 | /* |
163 | * Don't update the neighbor cache, if src = ::. |
164 | * This indicates that the src has no IP address assigned yet. |
165 | */ |
166 | if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) |
167 | goto freeit; |
168 | |
169 | IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len); |
170 | if (nd_rs == NULL) { |
171 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
172 | return; |
173 | } |
174 | |
175 | icmp6len -= sizeof(*nd_rs); |
176 | nd6_option_init(nd_rs + 1, icmp6len, &ndopts); |
177 | if (nd6_options(&ndopts) < 0) { |
178 | nd6log(LOG_INFO, "invalid ND option, ignored\n" ); |
179 | /* nd6_options have incremented stats */ |
180 | goto freeit; |
181 | } |
182 | |
183 | if (ndopts.nd_opts_src_lladdr) { |
184 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); |
185 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; |
186 | } |
187 | |
188 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
189 | nd6log(LOG_INFO, "lladdrlen mismatch for %s " |
190 | "(if %d, RS packet %d)\n" , |
191 | ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); |
192 | goto bad; |
193 | } |
194 | |
195 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); |
196 | |
197 | freeit: |
198 | m_put_rcvif_psref(ifp, &psref); |
199 | m_freem(m); |
200 | return; |
201 | |
202 | bad: |
203 | ICMP6_STATINC(ICMP6_STAT_BADRS); |
204 | m_put_rcvif_psref(ifp, &psref); |
205 | m_freem(m); |
206 | } |
207 | |
208 | /* |
209 | * Receive Router Advertisement Message. |
210 | * |
211 | * Based on RFC 2461 |
212 | * TODO: on-link bit on prefix information |
213 | * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing |
214 | */ |
215 | void |
216 | nd6_ra_input(struct mbuf *m, int off, int icmp6len) |
217 | { |
218 | struct ifnet *ifp; |
219 | struct nd_ifinfo *ndi; |
220 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
221 | struct nd_router_advert *nd_ra; |
222 | struct in6_addr saddr6 = ip6->ip6_src; |
223 | #if 0 |
224 | struct in6_addr daddr6 = ip6->ip6_dst; |
225 | int flags; /* = nd_ra->nd_ra_flags_reserved; */ |
226 | int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0); |
227 | int is_other = ((flags & ND_RA_FLAG_OTHER) != 0); |
228 | #endif |
229 | int mcast = 0; |
230 | union nd_opts ndopts; |
231 | struct nd_defrouter *dr; |
232 | struct psref psref; |
233 | |
234 | ifp = m_get_rcvif_psref(m, &psref); |
235 | if (ifp == NULL) |
236 | goto freeit; |
237 | |
238 | ndi = ND_IFINFO(ifp); |
239 | /* |
240 | * We only accept RAs when |
241 | * the system-wide variable allows the acceptance, and the |
242 | * per-interface variable allows RAs on the receiving interface. |
243 | */ |
244 | if (!nd6_accepts_rtadv(ndi)) |
245 | goto freeit; |
246 | |
247 | if (ip6->ip6_hlim != 255) { |
248 | nd6log(LOG_ERR, "invalid hlim (%d) from %s to %s on %s\n" , |
249 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), |
250 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp)); |
251 | goto bad; |
252 | } |
253 | |
254 | if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { |
255 | nd6log(LOG_ERR, "src %s is not link-local\n" , |
256 | ip6_sprintf(&saddr6)); |
257 | goto bad; |
258 | } |
259 | |
260 | IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); |
261 | if (nd_ra == NULL) { |
262 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
263 | m_put_rcvif_psref(ifp, &psref); |
264 | return; |
265 | } |
266 | |
267 | icmp6len -= sizeof(*nd_ra); |
268 | nd6_option_init(nd_ra + 1, icmp6len, &ndopts); |
269 | if (nd6_options(&ndopts) < 0) { |
270 | nd6log(LOG_INFO, "invalid ND option, ignored\n" ); |
271 | /* nd6_options have incremented stats */ |
272 | goto freeit; |
273 | } |
274 | |
275 | { |
276 | struct nd_defrouter drtr; |
277 | u_int32_t advreachable = nd_ra->nd_ra_reachable; |
278 | |
279 | /* remember if this is a multicasted advertisement */ |
280 | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) |
281 | mcast = 1; |
282 | |
283 | memset(&drtr, 0, sizeof(drtr)); |
284 | drtr.rtaddr = saddr6; |
285 | drtr.flags = nd_ra->nd_ra_flags_reserved; |
286 | drtr.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); |
287 | drtr.expire = time_uptime + drtr.rtlifetime; |
288 | drtr.ifp = ifp; |
289 | /* unspecified or not? (RFC 2461 6.3.4) */ |
290 | if (advreachable) { |
291 | NTOHL(advreachable); |
292 | if (advreachable <= MAX_REACHABLE_TIME && |
293 | ndi->basereachable != advreachable) { |
294 | ndi->basereachable = advreachable; |
295 | ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); |
296 | ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ |
297 | } |
298 | } |
299 | if (nd_ra->nd_ra_retransmit) |
300 | ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); |
301 | if (nd_ra->nd_ra_curhoplimit) { |
302 | if (ndi->chlim < nd_ra->nd_ra_curhoplimit) |
303 | ndi->chlim = nd_ra->nd_ra_curhoplimit; |
304 | else if (ndi->chlim != nd_ra->nd_ra_curhoplimit) |
305 | log(LOG_ERR, "nd_ra_input: lower CurHopLimit sent from " |
306 | "%s on %s (current=%d, received=%d), ignored\n" , |
307 | ip6_sprintf(&ip6->ip6_src), |
308 | if_name(ifp), ndi->chlim, nd_ra->nd_ra_curhoplimit); |
309 | } |
310 | dr = defrtrlist_update(&drtr); |
311 | } |
312 | |
313 | /* |
314 | * prefix |
315 | */ |
316 | if (ndopts.nd_opts_pi) { |
317 | struct nd_opt_hdr *pt; |
318 | struct nd_opt_prefix_info *pi = NULL; |
319 | struct nd_prefixctl prc; |
320 | |
321 | for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; |
322 | pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; |
323 | pt = (struct nd_opt_hdr *)((char *)pt + |
324 | (pt->nd_opt_len << 3))) { |
325 | if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) |
326 | continue; |
327 | pi = (struct nd_opt_prefix_info *)pt; |
328 | |
329 | if (pi->nd_opt_pi_len != 4) { |
330 | nd6log(LOG_INFO, "invalid option " |
331 | "len %d for prefix information option, " |
332 | "ignored\n" , pi->nd_opt_pi_len); |
333 | continue; |
334 | } |
335 | |
336 | if (128 < pi->nd_opt_pi_prefix_len) { |
337 | nd6log(LOG_INFO, "invalid prefix " |
338 | "len %d for prefix information option, " |
339 | "ignored\n" , pi->nd_opt_pi_prefix_len); |
340 | continue; |
341 | } |
342 | |
343 | if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) |
344 | || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { |
345 | nd6log(LOG_INFO, |
346 | "invalid prefix %s, ignored\n" , |
347 | ip6_sprintf(&pi->nd_opt_pi_prefix)); |
348 | continue; |
349 | } |
350 | |
351 | memset(&prc, 0, sizeof(prc)); |
352 | sockaddr_in6_init(&prc.ndprc_prefix, |
353 | &pi->nd_opt_pi_prefix, 0, 0, 0); |
354 | prc.ndprc_ifp = ifp; |
355 | |
356 | prc.ndprc_raf_onlink = (pi->nd_opt_pi_flags_reserved & |
357 | ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; |
358 | prc.ndprc_raf_auto = (pi->nd_opt_pi_flags_reserved & |
359 | ND_OPT_PI_FLAG_AUTO) ? 1 : 0; |
360 | prc.ndprc_plen = pi->nd_opt_pi_prefix_len; |
361 | prc.ndprc_vltime = ntohl(pi->nd_opt_pi_valid_time); |
362 | prc.ndprc_pltime = ntohl(pi->nd_opt_pi_preferred_time); |
363 | |
364 | (void)prelist_update(&prc, dr, m, mcast); |
365 | } |
366 | } |
367 | |
368 | /* |
369 | * MTU |
370 | */ |
371 | if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { |
372 | u_long mtu; |
373 | u_long maxmtu; |
374 | |
375 | mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); |
376 | |
377 | /* lower bound */ |
378 | if (mtu < IPV6_MMTU) { |
379 | nd6log(LOG_INFO, "bogus mtu option " |
380 | "mtu=%lu sent from %s, ignoring\n" , |
381 | mtu, ip6_sprintf(&ip6->ip6_src)); |
382 | goto skip; |
383 | } |
384 | |
385 | /* upper bound */ |
386 | maxmtu = (ndi->maxmtu && ndi->maxmtu < ifp->if_mtu) |
387 | ? ndi->maxmtu : ifp->if_mtu; |
388 | if (mtu <= maxmtu) { |
389 | int change = (ndi->linkmtu != mtu); |
390 | |
391 | ndi->linkmtu = mtu; |
392 | if (change) /* in6_maxmtu may change */ |
393 | in6_setmaxmtu(); |
394 | } else { |
395 | nd6log(LOG_INFO, |
396 | "bogus mtu mtu=%lu sent from %s; " |
397 | "exceeds maxmtu %lu, ignoring\n" , |
398 | mtu, ip6_sprintf(&ip6->ip6_src), maxmtu); |
399 | } |
400 | } |
401 | |
402 | skip: |
403 | |
404 | /* |
405 | * Source link layer address |
406 | */ |
407 | { |
408 | char *lladdr = NULL; |
409 | int lladdrlen = 0; |
410 | |
411 | if (ndopts.nd_opts_src_lladdr) { |
412 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); |
413 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; |
414 | } |
415 | |
416 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
417 | nd6log(LOG_INFO, "lladdrlen mismatch for %s " |
418 | "(if %d, RA packet %d)\n" , ip6_sprintf(&saddr6), |
419 | ifp->if_addrlen, lladdrlen - 2); |
420 | goto bad; |
421 | } |
422 | |
423 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); |
424 | |
425 | /* |
426 | * Installing a link-layer address might change the state of the |
427 | * router's neighbor cache, which might also affect our on-link |
428 | * detection of adveritsed prefixes. |
429 | */ |
430 | pfxlist_onlink_check(); |
431 | } |
432 | |
433 | freeit: |
434 | m_put_rcvif_psref(ifp, &psref); |
435 | m_freem(m); |
436 | return; |
437 | |
438 | bad: |
439 | ICMP6_STATINC(ICMP6_STAT_BADRA); |
440 | m_put_rcvif_psref(ifp, &psref); |
441 | m_freem(m); |
442 | } |
443 | |
444 | /* |
445 | * default router list processing sub routines |
446 | */ |
447 | void |
448 | defrouter_addreq(struct nd_defrouter *newdr) |
449 | { |
450 | union { |
451 | struct sockaddr_in6 sin6; |
452 | struct sockaddr sa; |
453 | } def, mask, gate; |
454 | int s; |
455 | int error; |
456 | |
457 | memset(&def, 0, sizeof(def)); |
458 | memset(&mask, 0, sizeof(mask)); |
459 | memset(&gate, 0,sizeof(gate)); /* for safety */ |
460 | |
461 | def.sin6.sin6_len = mask.sin6.sin6_len = gate.sin6.sin6_len = |
462 | sizeof(struct sockaddr_in6); |
463 | def.sin6.sin6_family = mask.sin6.sin6_family = gate.sin6.sin6_family = AF_INET6; |
464 | gate.sin6.sin6_addr = newdr->rtaddr; |
465 | #ifndef SCOPEDROUTING |
466 | gate.sin6.sin6_scope_id = 0; /* XXX */ |
467 | #endif |
468 | |
469 | s = splsoftnet(); |
470 | error = rtrequest_newmsg(RTM_ADD, &def.sa, &gate.sa, &mask.sa, |
471 | RTF_GATEWAY); |
472 | if (error == 0) { |
473 | nd6_numroutes++; |
474 | newdr->installed = 1; |
475 | } |
476 | splx(s); |
477 | return; |
478 | } |
479 | |
480 | struct nd_defrouter * |
481 | defrouter_lookup(const struct in6_addr *addr, struct ifnet *ifp) |
482 | { |
483 | struct nd_defrouter *dr; |
484 | |
485 | TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) { |
486 | if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) |
487 | break; |
488 | } |
489 | |
490 | return dr; /* search failed */ |
491 | } |
492 | |
493 | void |
494 | defrtrlist_del(struct nd_defrouter *dr, struct in6_ifextra *ext) |
495 | { |
496 | struct nd_defrouter *deldr = NULL; |
497 | struct nd_prefix *pr; |
498 | struct nd_ifinfo *ndi; |
499 | |
500 | if (ext == NULL) |
501 | ext = dr->ifp->if_afdata[AF_INET6]; |
502 | |
503 | /* detach already in progress, can not do anything */ |
504 | if (ext == NULL) |
505 | return; |
506 | |
507 | ndi = ext->nd_ifinfo; |
508 | |
509 | /* |
510 | * Flush all the routing table entries that use the router |
511 | * as a next hop. |
512 | */ |
513 | /* XXX: better condition? */ |
514 | if (!ip6_forwarding && nd6_accepts_rtadv(ndi)) |
515 | rt6_flush(&dr->rtaddr, dr->ifp); |
516 | |
517 | if (dr->installed) { |
518 | deldr = dr; |
519 | defrouter_delreq(dr); |
520 | } |
521 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); |
522 | |
523 | /* |
524 | * Also delete all the pointers to the router in each prefix lists. |
525 | */ |
526 | LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { |
527 | struct nd_pfxrouter *pfxrtr; |
528 | if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) |
529 | pfxrtr_del(pfxrtr); |
530 | } |
531 | pfxlist_onlink_check(); |
532 | |
533 | /* |
534 | * If the router is the primary one, choose a new one. |
535 | * Note that defrouter_select() will remove the current gateway |
536 | * from the routing table. |
537 | */ |
538 | if (deldr) |
539 | defrouter_select(); |
540 | |
541 | ext->ndefrouters--; |
542 | if (ext->ndefrouters < 0) { |
543 | log(LOG_WARNING, "defrtrlist_del: negative count on %s\n" , |
544 | dr->ifp->if_xname); |
545 | } |
546 | |
547 | free(dr, M_IP6NDP); |
548 | } |
549 | |
550 | /* |
551 | * Remove the default route for a given router. |
552 | * This is just a subroutine function for defrouter_select(), and should |
553 | * not be called from anywhere else. |
554 | */ |
555 | static void |
556 | defrouter_delreq(struct nd_defrouter *dr) |
557 | { |
558 | union { |
559 | struct sockaddr_in6 sin6; |
560 | struct sockaddr sa; |
561 | } def, mask, gw; |
562 | int error; |
563 | |
564 | #ifdef DIAGNOSTIC |
565 | if (dr == NULL) |
566 | panic("dr == NULL in defrouter_delreq" ); |
567 | #endif |
568 | |
569 | memset(&def, 0, sizeof(def)); |
570 | memset(&mask, 0, sizeof(mask)); |
571 | memset(&gw, 0, sizeof(gw)); /* for safety */ |
572 | |
573 | def.sin6.sin6_len = mask.sin6.sin6_len = gw.sin6.sin6_len = |
574 | sizeof(struct sockaddr_in6); |
575 | def.sin6.sin6_family = mask.sin6.sin6_family = gw.sin6.sin6_family = AF_INET6; |
576 | gw.sin6.sin6_addr = dr->rtaddr; |
577 | #ifndef SCOPEDROUTING |
578 | gw.sin6.sin6_scope_id = 0; /* XXX */ |
579 | #endif |
580 | |
581 | error = rtrequest_newmsg(RTM_DELETE, &def.sa, &gw.sa, &mask.sa, |
582 | RTF_GATEWAY); |
583 | if (error == 0) |
584 | nd6_numroutes--; |
585 | |
586 | dr->installed = 0; |
587 | } |
588 | |
589 | /* |
590 | * remove all default routes from default router list |
591 | */ |
592 | void |
593 | defrouter_reset(void) |
594 | { |
595 | struct nd_defrouter *dr; |
596 | |
597 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
598 | dr = TAILQ_NEXT(dr, dr_entry)) |
599 | defrouter_delreq(dr); |
600 | |
601 | /* |
602 | * XXX should we also nuke any default routers in the kernel, by |
603 | * going through them by rtalloc1()? |
604 | */ |
605 | } |
606 | |
607 | /* |
608 | * Default Router Selection according to Section 6.3.6 of RFC 2461 and |
609 | * draft-ietf-ipngwg-router-selection: |
610 | * 1) Routers that are reachable or probably reachable should be preferred. |
611 | * If we have more than one (probably) reachable router, prefer ones |
612 | * with the highest router preference. |
613 | * 2) When no routers on the list are known to be reachable or |
614 | * probably reachable, routers SHOULD be selected in a round-robin |
615 | * fashion, regardless of router preference values. |
616 | * 3) If the Default Router List is empty, assume that all |
617 | * destinations are on-link. |
618 | * |
619 | * We assume nd_defrouter is sorted by router preference value. |
620 | * Since the code below covers both with and without router preference cases, |
621 | * we do not need to classify the cases by ifdef. |
622 | * |
623 | * At this moment, we do not try to install more than one default router, |
624 | * even when the multipath routing is available, because we're not sure about |
625 | * the benefits for stub hosts comparing to the risk of making the code |
626 | * complicated and the possibility of introducing bugs. |
627 | */ |
628 | void |
629 | defrouter_select(void) |
630 | { |
631 | struct nd_ifinfo *ndi; |
632 | int s = splsoftnet(); |
633 | struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL; |
634 | |
635 | /* |
636 | * This function should be called only when acting as an autoconfigured |
637 | * host. Although the remaining part of this function is not effective |
638 | * if the node is not an autoconfigured host, we explicitly exclude |
639 | * such cases here for safety. |
640 | */ |
641 | if (ip6_forwarding) { |
642 | nd6log(LOG_WARNING, "called unexpectedly (forwarding=%d, " |
643 | "accept_rtadv=%d)\n" , ip6_forwarding, ip6_accept_rtadv); |
644 | splx(s); |
645 | return; |
646 | } |
647 | |
648 | /* |
649 | * Let's handle easy case (3) first: |
650 | * If default router list is empty, there's nothing to be done. |
651 | */ |
652 | if (!TAILQ_FIRST(&nd_defrouter)) { |
653 | splx(s); |
654 | return; |
655 | } |
656 | |
657 | /* |
658 | * Search for a (probably) reachable router from the list. |
659 | * We just pick up the first reachable one (if any), assuming that |
660 | * the ordering rule of the list described in defrtrlist_update(). |
661 | */ |
662 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
663 | dr = TAILQ_NEXT(dr, dr_entry)) { |
664 | ndi = ND_IFINFO(dr->ifp); |
665 | if (nd6_accepts_rtadv(ndi)) |
666 | continue; |
667 | |
668 | if (selected_dr == NULL && |
669 | nd6_is_llinfo_probreach(dr)) |
670 | selected_dr = dr; |
671 | |
672 | if (dr->installed && !installed_dr) |
673 | installed_dr = dr; |
674 | else if (dr->installed && installed_dr) { |
675 | /* this should not happen. warn for diagnosis. */ |
676 | log(LOG_ERR, "defrouter_select: more than one router" |
677 | " is installed\n" ); |
678 | } |
679 | } |
680 | /* |
681 | * If none of the default routers was found to be reachable, |
682 | * round-robin the list regardless of preference. |
683 | * Otherwise, if we have an installed router, check if the selected |
684 | * (reachable) router should really be preferred to the installed one. |
685 | * We only prefer the new router when the old one is not reachable |
686 | * or when the new one has a really higher preference value. |
687 | */ |
688 | if (selected_dr == NULL) { |
689 | if (installed_dr == NULL || !TAILQ_NEXT(installed_dr, dr_entry)) |
690 | selected_dr = TAILQ_FIRST(&nd_defrouter); |
691 | else |
692 | selected_dr = TAILQ_NEXT(installed_dr, dr_entry); |
693 | } else if (installed_dr && |
694 | nd6_is_llinfo_probreach(installed_dr) && |
695 | rtpref(selected_dr) <= rtpref(installed_dr)) { |
696 | selected_dr = installed_dr; |
697 | } |
698 | |
699 | /* |
700 | * If the selected router is different than the installed one, |
701 | * remove the installed router and install the selected one. |
702 | * Note that the selected router is never NULL here. |
703 | */ |
704 | if (installed_dr != selected_dr) { |
705 | if (installed_dr) |
706 | defrouter_delreq(installed_dr); |
707 | defrouter_addreq(selected_dr); |
708 | } |
709 | |
710 | splx(s); |
711 | return; |
712 | } |
713 | |
714 | /* |
715 | * for default router selection |
716 | * regards router-preference field as a 2-bit signed integer |
717 | */ |
718 | static int |
719 | rtpref(struct nd_defrouter *dr) |
720 | { |
721 | switch (dr->flags & ND_RA_FLAG_RTPREF_MASK) { |
722 | case ND_RA_FLAG_RTPREF_HIGH: |
723 | return (RTPREF_HIGH); |
724 | case ND_RA_FLAG_RTPREF_MEDIUM: |
725 | case ND_RA_FLAG_RTPREF_RSV: |
726 | return (RTPREF_MEDIUM); |
727 | case ND_RA_FLAG_RTPREF_LOW: |
728 | return (RTPREF_LOW); |
729 | default: |
730 | /* |
731 | * This case should never happen. If it did, it would mean a |
732 | * serious bug of kernel internal. We thus always bark here. |
733 | * Or, can we even panic? |
734 | */ |
735 | log(LOG_ERR, "rtpref: impossible RA flag %x\n" , dr->flags); |
736 | return (RTPREF_INVALID); |
737 | } |
738 | /* NOTREACHED */ |
739 | } |
740 | |
741 | static struct nd_defrouter * |
742 | defrtrlist_update(struct nd_defrouter *newdr) |
743 | { |
744 | struct nd_defrouter *dr, *n; |
745 | struct in6_ifextra *ext = newdr->ifp->if_afdata[AF_INET6]; |
746 | int s = splsoftnet(); |
747 | |
748 | if ((dr = defrouter_lookup(&newdr->rtaddr, newdr->ifp)) != NULL) { |
749 | /* entry exists */ |
750 | if (newdr->rtlifetime == 0) { |
751 | defrtrlist_del(dr, ext); |
752 | dr = NULL; |
753 | } else { |
754 | int oldpref = rtpref(dr); |
755 | |
756 | /* override */ |
757 | dr->flags = newdr->flags; /* xxx flag check */ |
758 | dr->rtlifetime = newdr->rtlifetime; |
759 | dr->expire = newdr->expire; |
760 | |
761 | /* |
762 | * If the preference does not change, there's no need |
763 | * to sort the entries. |
764 | */ |
765 | if (rtpref(newdr) == oldpref) { |
766 | splx(s); |
767 | return (dr); |
768 | } |
769 | |
770 | /* |
771 | * preferred router may be changed, so relocate |
772 | * this router. |
773 | * XXX: calling TAILQ_REMOVE directly is a bad manner. |
774 | * However, since defrtrlist_del() has many side |
775 | * effects, we intentionally do so here. |
776 | * defrouter_select() below will handle routing |
777 | * changes later. |
778 | */ |
779 | TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); |
780 | n = dr; |
781 | goto insert; |
782 | } |
783 | splx(s); |
784 | return (dr); |
785 | } |
786 | |
787 | if (ip6_maxifdefrouters >= 0 && |
788 | ext->ndefrouters >= ip6_maxifdefrouters) { |
789 | splx(s); |
790 | return (NULL); |
791 | } |
792 | |
793 | /* entry does not exist */ |
794 | if (newdr->rtlifetime == 0) { |
795 | splx(s); |
796 | return (NULL); |
797 | } |
798 | |
799 | if (ip6_rtadv_maxroutes <= nd6_numroutes) { |
800 | ICMP6_STATINC(ICMP6_STAT_DROPPED_RAROUTE); |
801 | splx(s); |
802 | return (NULL); |
803 | } |
804 | |
805 | n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT); |
806 | if (n == NULL) { |
807 | splx(s); |
808 | return (NULL); |
809 | } |
810 | memset(n, 0, sizeof(*n)); |
811 | *n = *newdr; |
812 | |
813 | insert: |
814 | /* |
815 | * Insert the new router in the Default Router List; |
816 | * The Default Router List should be in the descending order |
817 | * of router-preferece. Routers with the same preference are |
818 | * sorted in the arriving time order. |
819 | */ |
820 | |
821 | /* insert at the end of the group */ |
822 | for (dr = TAILQ_FIRST(&nd_defrouter); dr; |
823 | dr = TAILQ_NEXT(dr, dr_entry)) { |
824 | if (rtpref(n) > rtpref(dr)) |
825 | break; |
826 | } |
827 | if (dr) |
828 | TAILQ_INSERT_BEFORE(dr, n, dr_entry); |
829 | else |
830 | TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); |
831 | |
832 | defrouter_select(); |
833 | |
834 | ext->ndefrouters++; |
835 | |
836 | splx(s); |
837 | |
838 | return (n); |
839 | } |
840 | |
841 | static struct nd_pfxrouter * |
842 | pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) |
843 | { |
844 | struct nd_pfxrouter *search; |
845 | |
846 | LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) { |
847 | if (search->router == dr) |
848 | break; |
849 | } |
850 | |
851 | return (search); |
852 | } |
853 | |
854 | static void |
855 | pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr) |
856 | { |
857 | struct nd_pfxrouter *newpfr; |
858 | |
859 | newpfr = malloc(sizeof(*newpfr), M_IP6NDP, M_NOWAIT|M_ZERO); |
860 | if (newpfr == NULL) |
861 | return; |
862 | newpfr->router = dr; |
863 | |
864 | LIST_INSERT_HEAD(&pr->ndpr_advrtrs, newpfr, pfr_entry); |
865 | |
866 | pfxlist_onlink_check(); |
867 | } |
868 | |
869 | static void |
870 | pfxrtr_del(struct nd_pfxrouter *pfr) |
871 | { |
872 | LIST_REMOVE(pfr, pfr_entry); |
873 | free(pfr, M_IP6NDP); |
874 | } |
875 | |
876 | struct nd_prefix * |
877 | nd6_prefix_lookup(struct nd_prefixctl *key) |
878 | { |
879 | struct nd_prefix *search; |
880 | |
881 | LIST_FOREACH(search, &nd_prefix, ndpr_entry) { |
882 | if (key->ndprc_ifp == search->ndpr_ifp && |
883 | key->ndprc_plen == search->ndpr_plen && |
884 | in6_are_prefix_equal(&key->ndprc_prefix.sin6_addr, |
885 | &search->ndpr_prefix.sin6_addr, key->ndprc_plen)) { |
886 | break; |
887 | } |
888 | } |
889 | |
890 | return (search); |
891 | } |
892 | |
893 | static void |
894 | purge_detached(struct ifnet *ifp) |
895 | { |
896 | struct nd_prefix *pr, *pr_next; |
897 | struct in6_ifaddr *ia; |
898 | struct ifaddr *ifa, *ifa_next; |
899 | |
900 | for (pr = nd_prefix.lh_first; pr; pr = pr_next) { |
901 | int s; |
902 | pr_next = pr->ndpr_next; |
903 | |
904 | /* |
905 | * This function is called when we need to make more room for |
906 | * new prefixes rather than keeping old, possibly stale ones. |
907 | * Detached prefixes would be a good candidate; if all routers |
908 | * that advertised the prefix expired, the prefix is also |
909 | * probably stale. |
910 | */ |
911 | if (pr->ndpr_ifp != ifp || |
912 | IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) || |
913 | ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && |
914 | !LIST_EMPTY(&pr->ndpr_advrtrs))) |
915 | continue; |
916 | |
917 | restart: |
918 | s = pserialize_read_enter(); |
919 | for (ifa = IFADDR_READER_FIRST(ifp); ifa; ifa = ifa_next) { |
920 | ifa_next = IFADDR_READER_NEXT(ifa); |
921 | if (ifa->ifa_addr->sa_family != AF_INET6) |
922 | continue; |
923 | ia = (struct in6_ifaddr *)ifa; |
924 | if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == |
925 | IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) { |
926 | pserialize_read_exit(s); |
927 | in6_purgeaddr(ifa); |
928 | goto restart; |
929 | } |
930 | } |
931 | pserialize_read_exit(s); |
932 | |
933 | if (pr->ndpr_refcnt == 0) |
934 | prelist_remove(pr); |
935 | } |
936 | } |
937 | int |
938 | nd6_prelist_add(struct nd_prefixctl *prc, struct nd_defrouter *dr, |
939 | struct nd_prefix **newp) |
940 | { |
941 | struct nd_prefix *newpr = NULL; |
942 | int i, s; |
943 | int error; |
944 | struct in6_ifextra *ext = prc->ndprc_ifp->if_afdata[AF_INET6]; |
945 | |
946 | if (ip6_maxifprefixes >= 0) { |
947 | if (ext->nprefixes >= ip6_maxifprefixes / 2) |
948 | purge_detached(prc->ndprc_ifp); |
949 | if (ext->nprefixes >= ip6_maxifprefixes) |
950 | return ENOMEM; |
951 | } |
952 | |
953 | error = 0; |
954 | newpr = malloc(sizeof(*newpr), M_IP6NDP, M_NOWAIT|M_ZERO); |
955 | if (newpr == NULL) |
956 | return ENOMEM; |
957 | newpr->ndpr_ifp = prc->ndprc_ifp; |
958 | newpr->ndpr_prefix = prc->ndprc_prefix; |
959 | newpr->ndpr_plen = prc->ndprc_plen; |
960 | newpr->ndpr_vltime = prc->ndprc_vltime; |
961 | newpr->ndpr_pltime = prc->ndprc_pltime; |
962 | newpr->ndpr_flags = prc->ndprc_flags; |
963 | if ((error = in6_init_prefix_ltimes(newpr)) != 0) { |
964 | free(newpr, M_IP6NDP); |
965 | return(error); |
966 | } |
967 | newpr->ndpr_lastupdate = time_uptime; |
968 | if (newp != NULL) |
969 | *newp = newpr; |
970 | |
971 | /* initialization */ |
972 | LIST_INIT(&newpr->ndpr_advrtrs); |
973 | in6_prefixlen2mask(&newpr->ndpr_mask, newpr->ndpr_plen); |
974 | /* make prefix in the canonical form */ |
975 | for (i = 0; i < 4; i++) { |
976 | newpr->ndpr_prefix.sin6_addr.s6_addr32[i] &= |
977 | newpr->ndpr_mask.s6_addr32[i]; |
978 | } |
979 | |
980 | s = splsoftnet(); |
981 | /* link ndpr_entry to nd_prefix list */ |
982 | LIST_INSERT_HEAD(&nd_prefix, newpr, ndpr_entry); |
983 | splx(s); |
984 | |
985 | /* ND_OPT_PI_FLAG_ONLINK processing */ |
986 | if (newpr->ndpr_raf_onlink) { |
987 | int e; |
988 | |
989 | if ((e = nd6_prefix_onlink(newpr)) != 0) { |
990 | nd6log(LOG_ERR, "failed to make " |
991 | "the prefix %s/%d on-link on %s (errno=%d)\n" , |
992 | ip6_sprintf(&prc->ndprc_prefix.sin6_addr), |
993 | prc->ndprc_plen, if_name(prc->ndprc_ifp), e); |
994 | /* proceed anyway. XXX: is it correct? */ |
995 | } |
996 | } |
997 | |
998 | if (dr) |
999 | pfxrtr_add(newpr, dr); |
1000 | |
1001 | ext->nprefixes++; |
1002 | |
1003 | return 0; |
1004 | } |
1005 | |
1006 | void |
1007 | prelist_remove(struct nd_prefix *pr) |
1008 | { |
1009 | struct nd_pfxrouter *pfr, *next; |
1010 | int e, s; |
1011 | struct in6_ifextra *ext = pr->ndpr_ifp->if_afdata[AF_INET6]; |
1012 | |
1013 | /* make sure to invalidate the prefix until it is really freed. */ |
1014 | pr->ndpr_vltime = 0; |
1015 | pr->ndpr_pltime = 0; |
1016 | #if 0 |
1017 | /* |
1018 | * Though these flags are now meaningless, we'd rather keep the value |
1019 | * not to confuse users when executing "ndp -p". |
1020 | */ |
1021 | pr->ndpr_raf_onlink = 0; |
1022 | pr->ndpr_raf_auto = 0; |
1023 | #endif |
1024 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 && |
1025 | (e = nd6_prefix_offlink(pr)) != 0) { |
1026 | nd6log(LOG_ERR, |
1027 | "failed to make %s/%d offlink on %s, errno=%d\n" , |
1028 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
1029 | pr->ndpr_plen, if_name(pr->ndpr_ifp), e); |
1030 | /* what should we do? */ |
1031 | } |
1032 | |
1033 | if (pr->ndpr_refcnt > 0) |
1034 | return; /* notice here? */ |
1035 | |
1036 | s = splsoftnet(); |
1037 | /* unlink ndpr_entry from nd_prefix list */ |
1038 | LIST_REMOVE(pr, ndpr_entry); |
1039 | |
1040 | /* free list of routers that adversed the prefix */ |
1041 | for (pfr = LIST_FIRST(&pr->ndpr_advrtrs); pfr != NULL; pfr = next) { |
1042 | next = LIST_NEXT(pfr, pfr_entry); |
1043 | |
1044 | free(pfr, M_IP6NDP); |
1045 | } |
1046 | |
1047 | if (ext) { |
1048 | ext->nprefixes--; |
1049 | if (ext->nprefixes < 0) { |
1050 | log(LOG_WARNING, "prelist_remove: negative count on " |
1051 | "%s\n" , pr->ndpr_ifp->if_xname); |
1052 | } |
1053 | } |
1054 | splx(s); |
1055 | |
1056 | free(pr, M_IP6NDP); |
1057 | |
1058 | pfxlist_onlink_check(); |
1059 | } |
1060 | |
1061 | static int |
1062 | prelist_update(struct nd_prefixctl *newprc, |
1063 | struct nd_defrouter *dr, /* may be NULL */ |
1064 | struct mbuf *m, |
1065 | int mcast) |
1066 | { |
1067 | struct in6_ifaddr *ia6_match = NULL; |
1068 | struct ifaddr *ifa; |
1069 | struct ifnet *ifp = newprc->ndprc_ifp; |
1070 | struct nd_prefix *pr; |
1071 | int s = splsoftnet(); |
1072 | int error = 0; |
1073 | int auth; |
1074 | struct in6_addrlifetime lt6_tmp; |
1075 | int ss; |
1076 | |
1077 | auth = 0; |
1078 | if (m) { |
1079 | /* |
1080 | * Authenticity for NA consists authentication for |
1081 | * both IP header and IP datagrams, doesn't it ? |
1082 | */ |
1083 | #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) |
1084 | auth = (m->m_flags & M_AUTHIPHDR |
1085 | && m->m_flags & M_AUTHIPDGM) ? 1 : 0; |
1086 | #endif |
1087 | } |
1088 | |
1089 | if ((pr = nd6_prefix_lookup(newprc)) != NULL) { |
1090 | /* |
1091 | * nd6_prefix_lookup() ensures that pr and newprc have the same |
1092 | * prefix on a same interface. |
1093 | */ |
1094 | |
1095 | /* |
1096 | * Update prefix information. Note that the on-link (L) bit |
1097 | * and the autonomous (A) bit should NOT be changed from 1 |
1098 | * to 0. |
1099 | */ |
1100 | if (newprc->ndprc_raf_onlink == 1) |
1101 | pr->ndpr_raf_onlink = 1; |
1102 | if (newprc->ndprc_raf_auto == 1) |
1103 | pr->ndpr_raf_auto = 1; |
1104 | if (newprc->ndprc_raf_onlink) { |
1105 | pr->ndpr_vltime = newprc->ndprc_vltime; |
1106 | pr->ndpr_pltime = newprc->ndprc_pltime; |
1107 | (void)in6_init_prefix_ltimes(pr); /* XXX error case? */ |
1108 | pr->ndpr_lastupdate = time_uptime; |
1109 | } |
1110 | |
1111 | if (newprc->ndprc_raf_onlink && |
1112 | (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { |
1113 | int e; |
1114 | |
1115 | if ((e = nd6_prefix_onlink(pr)) != 0) { |
1116 | nd6log(LOG_ERR, |
1117 | "failed to make " |
1118 | "the prefix %s/%d on-link on %s " |
1119 | "(errno=%d)\n" , |
1120 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
1121 | pr->ndpr_plen, if_name(pr->ndpr_ifp), e); |
1122 | /* proceed anyway. XXX: is it correct? */ |
1123 | } |
1124 | } |
1125 | |
1126 | if (dr && pfxrtr_lookup(pr, dr) == NULL) |
1127 | pfxrtr_add(pr, dr); |
1128 | } else { |
1129 | struct nd_prefix *newpr = NULL; |
1130 | |
1131 | if (newprc->ndprc_vltime == 0) |
1132 | goto end; |
1133 | if (newprc->ndprc_raf_onlink == 0 && newprc->ndprc_raf_auto == 0) |
1134 | goto end; |
1135 | |
1136 | if (ip6_rtadv_maxroutes <= nd6_numroutes) { |
1137 | ICMP6_STATINC(ICMP6_STAT_DROPPED_RAROUTE); |
1138 | goto end; |
1139 | } |
1140 | |
1141 | error = nd6_prelist_add(newprc, dr, &newpr); |
1142 | if (error != 0 || newpr == NULL) { |
1143 | nd6log(LOG_NOTICE, |
1144 | "nd6_prelist_add failed for %s/%d on %s " |
1145 | "errno=%d, returnpr=%p\n" , |
1146 | ip6_sprintf(&newprc->ndprc_prefix.sin6_addr), |
1147 | newprc->ndprc_plen, if_name(newprc->ndprc_ifp), |
1148 | error, newpr); |
1149 | goto end; /* we should just give up in this case. */ |
1150 | } |
1151 | |
1152 | /* |
1153 | * XXX: from the ND point of view, we can ignore a prefix |
1154 | * with the on-link bit being zero. However, we need a |
1155 | * prefix structure for references from autoconfigured |
1156 | * addresses. Thus, we explicitly make sure that the prefix |
1157 | * itself expires now. |
1158 | */ |
1159 | if (newpr->ndpr_raf_onlink == 0) { |
1160 | newpr->ndpr_vltime = 0; |
1161 | newpr->ndpr_pltime = 0; |
1162 | in6_init_prefix_ltimes(newpr); |
1163 | } |
1164 | |
1165 | pr = newpr; |
1166 | } |
1167 | |
1168 | /* |
1169 | * Address autoconfiguration based on Section 5.5.3 of RFC 2462. |
1170 | * Note that pr must be non NULL at this point. |
1171 | */ |
1172 | |
1173 | /* 5.5.3 (a). Ignore the prefix without the A bit set. */ |
1174 | if (!newprc->ndprc_raf_auto) |
1175 | goto end; |
1176 | |
1177 | /* |
1178 | * 5.5.3 (b). the link-local prefix should have been ignored in |
1179 | * nd6_ra_input. |
1180 | */ |
1181 | |
1182 | /* 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. */ |
1183 | if (newprc->ndprc_pltime > newprc->ndprc_vltime) { |
1184 | error = EINVAL; /* XXX: won't be used */ |
1185 | goto end; |
1186 | } |
1187 | |
1188 | /* |
1189 | * 5.5.3 (d). If the prefix advertised is not equal to the prefix of |
1190 | * an address configured by stateless autoconfiguration already in the |
1191 | * list of addresses associated with the interface, and the Valid |
1192 | * Lifetime is not 0, form an address. We first check if we have |
1193 | * a matching prefix. |
1194 | * Note: we apply a clarification in rfc2462bis-02 here. We only |
1195 | * consider autoconfigured addresses while RFC2462 simply said |
1196 | * "address". |
1197 | */ |
1198 | ss = pserialize_read_enter(); |
1199 | IFADDR_READER_FOREACH(ifa, ifp) { |
1200 | struct in6_ifaddr *ia6; |
1201 | u_int32_t remaininglifetime; |
1202 | |
1203 | if (ifa->ifa_addr->sa_family != AF_INET6) |
1204 | continue; |
1205 | |
1206 | ia6 = (struct in6_ifaddr *)ifa; |
1207 | |
1208 | /* |
1209 | * We only consider autoconfigured addresses as per rfc2462bis. |
1210 | */ |
1211 | if (!(ia6->ia6_flags & IN6_IFF_AUTOCONF)) |
1212 | continue; |
1213 | |
1214 | /* |
1215 | * Spec is not clear here, but I believe we should concentrate |
1216 | * on unicast (i.e. not anycast) addresses. |
1217 | * XXX: other ia6_flags? detached or duplicated? |
1218 | */ |
1219 | if ((ia6->ia6_flags & IN6_IFF_ANYCAST) != 0) |
1220 | continue; |
1221 | |
1222 | /* |
1223 | * Ignore the address if it is not associated with a prefix |
1224 | * or is associated with a prefix that is different from this |
1225 | * one. (pr is never NULL here) |
1226 | */ |
1227 | if (ia6->ia6_ndpr != pr) |
1228 | continue; |
1229 | |
1230 | if (ia6_match == NULL) /* remember the first one */ |
1231 | ia6_match = ia6; |
1232 | |
1233 | /* |
1234 | * An already autoconfigured address matched. Now that we |
1235 | * are sure there is at least one matched address, we can |
1236 | * proceed to 5.5.3. (e): update the lifetimes according to the |
1237 | * "two hours" rule and the privacy extension. |
1238 | * We apply some clarifications in rfc2462bis: |
1239 | * - use remaininglifetime instead of storedlifetime as a |
1240 | * variable name |
1241 | * - remove the dead code in the "two-hour" rule |
1242 | */ |
1243 | #define TWOHOUR (120*60) |
1244 | lt6_tmp = ia6->ia6_lifetime; |
1245 | if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME) |
1246 | remaininglifetime = ND6_INFINITE_LIFETIME; |
1247 | else if (time_uptime - ia6->ia6_updatetime > |
1248 | lt6_tmp.ia6t_vltime) { |
1249 | /* |
1250 | * The case of "invalid" address. We should usually |
1251 | * not see this case. |
1252 | */ |
1253 | remaininglifetime = 0; |
1254 | } else |
1255 | remaininglifetime = lt6_tmp.ia6t_vltime - |
1256 | (time_uptime - ia6->ia6_updatetime); |
1257 | |
1258 | /* when not updating, keep the current stored lifetime. */ |
1259 | lt6_tmp.ia6t_vltime = remaininglifetime; |
1260 | |
1261 | if (TWOHOUR < newprc->ndprc_vltime || |
1262 | remaininglifetime < newprc->ndprc_vltime) { |
1263 | lt6_tmp.ia6t_vltime = newprc->ndprc_vltime; |
1264 | } else if (remaininglifetime <= TWOHOUR) { |
1265 | if (auth) |
1266 | lt6_tmp.ia6t_vltime = newprc->ndprc_vltime; |
1267 | } else { |
1268 | /* |
1269 | * newprc->ndprc_vltime <= TWOHOUR && |
1270 | * TWOHOUR < remaininglifetime |
1271 | */ |
1272 | lt6_tmp.ia6t_vltime = TWOHOUR; |
1273 | } |
1274 | |
1275 | /* The 2 hour rule is not imposed for preferred lifetime. */ |
1276 | lt6_tmp.ia6t_pltime = newprc->ndprc_pltime; |
1277 | |
1278 | in6_init_address_ltimes(pr, <6_tmp); |
1279 | |
1280 | /* |
1281 | * We need to treat lifetimes for temporary addresses |
1282 | * differently, according to |
1283 | * draft-ietf-ipv6-privacy-addrs-v2-01.txt 3.3 (1); |
1284 | * we only update the lifetimes when they are in the maximum |
1285 | * intervals. |
1286 | */ |
1287 | if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { |
1288 | u_int32_t maxvltime, maxpltime; |
1289 | |
1290 | if (ip6_temp_valid_lifetime > |
1291 | (u_int32_t)((time_uptime - ia6->ia6_createtime) + |
1292 | ip6_desync_factor)) { |
1293 | maxvltime = ip6_temp_valid_lifetime - |
1294 | (time_uptime - ia6->ia6_createtime) - |
1295 | ip6_desync_factor; |
1296 | } else |
1297 | maxvltime = 0; |
1298 | if (ip6_temp_preferred_lifetime > |
1299 | (u_int32_t)((time_uptime - ia6->ia6_createtime) + |
1300 | ip6_desync_factor)) { |
1301 | maxpltime = ip6_temp_preferred_lifetime - |
1302 | (time_uptime - ia6->ia6_createtime) - |
1303 | ip6_desync_factor; |
1304 | } else |
1305 | maxpltime = 0; |
1306 | |
1307 | if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME || |
1308 | lt6_tmp.ia6t_vltime > maxvltime) { |
1309 | lt6_tmp.ia6t_vltime = maxvltime; |
1310 | } |
1311 | if (lt6_tmp.ia6t_pltime == ND6_INFINITE_LIFETIME || |
1312 | lt6_tmp.ia6t_pltime > maxpltime) { |
1313 | lt6_tmp.ia6t_pltime = maxpltime; |
1314 | } |
1315 | } |
1316 | |
1317 | ia6->ia6_lifetime = lt6_tmp; |
1318 | ia6->ia6_updatetime = time_uptime; |
1319 | } |
1320 | pserialize_read_exit(ss); |
1321 | |
1322 | if (ia6_match == NULL && newprc->ndprc_vltime) { |
1323 | int ifidlen; |
1324 | struct in6_ifaddr *ia6; |
1325 | struct psref psref; |
1326 | |
1327 | /* |
1328 | * 5.5.3 (d) (continued) |
1329 | * No address matched and the valid lifetime is non-zero. |
1330 | * Create a new address. |
1331 | */ |
1332 | |
1333 | /* |
1334 | * Prefix Length check: |
1335 | * If the sum of the prefix length and interface identifier |
1336 | * length does not equal 128 bits, the Prefix Information |
1337 | * option MUST be ignored. The length of the interface |
1338 | * identifier is defined in a separate link-type specific |
1339 | * document. |
1340 | */ |
1341 | ifidlen = in6_if2idlen(ifp); |
1342 | if (ifidlen < 0) { |
1343 | /* this should not happen, so we always log it. */ |
1344 | log(LOG_ERR, "%s: IFID undefined (%s)\n" , |
1345 | __func__, if_name(ifp)); |
1346 | goto end; |
1347 | } |
1348 | if (ifidlen + pr->ndpr_plen != 128) { |
1349 | nd6log(LOG_INFO, |
1350 | "invalid prefixlen %d for %s, ignored\n" , |
1351 | pr->ndpr_plen, if_name(ifp)); |
1352 | goto end; |
1353 | } |
1354 | |
1355 | if ((ia6 = in6_ifadd(newprc, mcast, &psref)) != NULL) { |
1356 | /* |
1357 | * note that we should use pr (not newprc) for reference. |
1358 | */ |
1359 | pr->ndpr_refcnt++; |
1360 | ia6->ia6_ndpr = pr; |
1361 | |
1362 | /* toggle onlink state if the address was assigned |
1363 | * a prefix route. */ |
1364 | if (ia6->ia_flags & IFA_ROUTE) |
1365 | pr->ndpr_stateflags |= NDPRF_ONLINK; |
1366 | |
1367 | /* |
1368 | * draft-ietf-ipngwg-temp-addresses-v2-00 3.3 (2). |
1369 | * When a new public address is created as described |
1370 | * in RFC2462, also create a new temporary address. |
1371 | * |
1372 | * draft-ietf-ipngwg-temp-addresses-v2-00 3.5. |
1373 | * When an interface connects to a new link, a new |
1374 | * randomized interface identifier should be generated |
1375 | * immediately together with a new set of temporary |
1376 | * addresses. Thus, we specifiy 1 as the 2nd arg of |
1377 | * in6_tmpifadd(). |
1378 | */ |
1379 | if (ip6_use_tempaddr) { |
1380 | int e; |
1381 | if ((e = in6_tmpifadd(ia6, 1, 1)) != 0) { |
1382 | nd6log(LOG_NOTICE, |
1383 | "failed to create a temporary " |
1384 | "address, errno=%d\n" , e); |
1385 | } |
1386 | } |
1387 | ia6_release(ia6, &psref); |
1388 | |
1389 | /* |
1390 | * A newly added address might affect the status |
1391 | * of other addresses, so we check and update it. |
1392 | * XXX: what if address duplication happens? |
1393 | */ |
1394 | pfxlist_onlink_check(); |
1395 | } else { |
1396 | /* just set an error. do not bark here. */ |
1397 | error = EADDRNOTAVAIL; /* XXX: might be unused. */ |
1398 | } |
1399 | } |
1400 | |
1401 | end: |
1402 | splx(s); |
1403 | return error; |
1404 | } |
1405 | |
1406 | /* |
1407 | * A supplement function used in the on-link detection below; |
1408 | * detect if a given prefix has a (probably) reachable advertising router. |
1409 | * XXX: lengthy function name... |
1410 | */ |
1411 | static struct nd_pfxrouter * |
1412 | find_pfxlist_reachable_router(struct nd_prefix *pr) |
1413 | { |
1414 | struct nd_pfxrouter *pfxrtr; |
1415 | |
1416 | for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr; |
1417 | pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) { |
1418 | if (pfxrtr->router->ifp->if_flags & IFF_UP && |
1419 | pfxrtr->router->ifp->if_link_state != LINK_STATE_DOWN && |
1420 | nd6_is_llinfo_probreach(pfxrtr->router)) |
1421 | break; /* found */ |
1422 | } |
1423 | |
1424 | return (pfxrtr); |
1425 | } |
1426 | |
1427 | /* |
1428 | * Check if each prefix in the prefix list has at least one available router |
1429 | * that advertised the prefix (a router is "available" if its neighbor cache |
1430 | * entry is reachable or probably reachable). |
1431 | * If the check fails, the prefix may be off-link, because, for example, |
1432 | * we have moved from the network but the lifetime of the prefix has not |
1433 | * expired yet. So we should not use the prefix if there is another prefix |
1434 | * that has an available router. |
1435 | * But, if there is no prefix that has an available router, we still regards |
1436 | * all the prefixes as on-link. This is because we can't tell if all the |
1437 | * routers are simply dead or if we really moved from the network and there |
1438 | * is no router around us. |
1439 | */ |
1440 | void |
1441 | pfxlist_onlink_check(void) |
1442 | { |
1443 | struct nd_prefix *pr; |
1444 | struct in6_ifaddr *ia; |
1445 | struct nd_defrouter *dr; |
1446 | struct nd_pfxrouter *pfxrtr = NULL; |
1447 | int s; |
1448 | |
1449 | /* |
1450 | * Check if there is a prefix that has a reachable advertising |
1451 | * router. |
1452 | */ |
1453 | LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { |
1454 | if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) |
1455 | break; |
1456 | } |
1457 | /* |
1458 | * If we have no such prefix, check whether we still have a router |
1459 | * that does not advertise any prefixes. |
1460 | */ |
1461 | if (pr == NULL) { |
1462 | TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) { |
1463 | struct nd_prefix *pr0; |
1464 | |
1465 | LIST_FOREACH(pr0, &nd_prefix, ndpr_entry) { |
1466 | if ((pfxrtr = pfxrtr_lookup(pr0, dr)) != NULL) |
1467 | break; |
1468 | } |
1469 | if (pfxrtr) |
1470 | break; |
1471 | } |
1472 | } |
1473 | if (pr != NULL || (TAILQ_FIRST(&nd_defrouter) && !pfxrtr)) { |
1474 | /* |
1475 | * There is at least one prefix that has a reachable router, |
1476 | * or at least a router which probably does not advertise |
1477 | * any prefixes. The latter would be the case when we move |
1478 | * to a new link where we have a router that does not provide |
1479 | * prefixes and we configure an address by hand. |
1480 | * Detach prefixes which have no reachable advertising |
1481 | * router, and attach other prefixes. |
1482 | */ |
1483 | LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { |
1484 | /* XXX: a link-local prefix should never be detached */ |
1485 | if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) |
1486 | continue; |
1487 | |
1488 | /* |
1489 | * we aren't interested in prefixes without the L bit |
1490 | * set. |
1491 | */ |
1492 | if (pr->ndpr_raf_onlink == 0) |
1493 | continue; |
1494 | |
1495 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && |
1496 | find_pfxlist_reachable_router(pr) == NULL) |
1497 | pr->ndpr_stateflags |= NDPRF_DETACHED; |
1498 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && |
1499 | find_pfxlist_reachable_router(pr) != 0) |
1500 | pr->ndpr_stateflags &= ~NDPRF_DETACHED; |
1501 | } |
1502 | } else { |
1503 | /* there is no prefix that has a reachable router */ |
1504 | LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { |
1505 | if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) |
1506 | continue; |
1507 | |
1508 | if (pr->ndpr_raf_onlink == 0) |
1509 | continue; |
1510 | |
1511 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) |
1512 | pr->ndpr_stateflags &= ~NDPRF_DETACHED; |
1513 | } |
1514 | } |
1515 | |
1516 | /* |
1517 | * Remove each interface route associated with a (just) detached |
1518 | * prefix, and reinstall the interface route for a (just) attached |
1519 | * prefix. Note that all attempt of reinstallation does not |
1520 | * necessarily success, when a same prefix is shared among multiple |
1521 | * interfaces. Such cases will be handled in nd6_prefix_onlink, |
1522 | * so we don't have to care about them. |
1523 | */ |
1524 | LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { |
1525 | int e; |
1526 | |
1527 | if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) |
1528 | continue; |
1529 | |
1530 | if (pr->ndpr_raf_onlink == 0) |
1531 | continue; |
1532 | |
1533 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && |
1534 | (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { |
1535 | if ((e = nd6_prefix_offlink(pr)) != 0) { |
1536 | nd6log(LOG_ERR, |
1537 | "failed to make %s/%d offlink, errno=%d\n" , |
1538 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
1539 | pr->ndpr_plen, e); |
1540 | } |
1541 | } |
1542 | if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && |
1543 | (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && |
1544 | pr->ndpr_raf_onlink) { |
1545 | if ((e = nd6_prefix_onlink(pr)) != 0) { |
1546 | nd6log(LOG_ERR, |
1547 | "failed to make %s/%d onlink, errno=%d\n" , |
1548 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
1549 | pr->ndpr_plen, e); |
1550 | } |
1551 | } |
1552 | } |
1553 | |
1554 | /* |
1555 | * Changes on the prefix status might affect address status as well. |
1556 | * Make sure that all addresses derived from an attached prefix are |
1557 | * attached, and that all addresses derived from a detached prefix are |
1558 | * detached. Note, however, that a manually configured address should |
1559 | * always be attached. |
1560 | * The precise detection logic is same as the one for prefixes. |
1561 | */ |
1562 | s = pserialize_read_enter(); |
1563 | IN6_ADDRLIST_READER_FOREACH(ia) { |
1564 | if (!(ia->ia6_flags & IN6_IFF_AUTOCONF)) |
1565 | continue; |
1566 | |
1567 | if (ia->ia6_ndpr == NULL) { |
1568 | /* |
1569 | * This can happen when we first configure the address |
1570 | * (i.e. the address exists, but the prefix does not). |
1571 | * XXX: complicated relationships... |
1572 | */ |
1573 | continue; |
1574 | } |
1575 | |
1576 | if (find_pfxlist_reachable_router(ia->ia6_ndpr)) |
1577 | break; |
1578 | } |
1579 | pserialize_read_exit(s); |
1580 | |
1581 | if (ia) { |
1582 | int bound = curlwp_bind(); |
1583 | |
1584 | s = pserialize_read_enter(); |
1585 | IN6_ADDRLIST_READER_FOREACH(ia) { |
1586 | struct ifaddr *ifa = (struct ifaddr *)ia; |
1587 | struct psref psref; |
1588 | |
1589 | if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) |
1590 | continue; |
1591 | |
1592 | if (ia->ia6_ndpr == NULL) /* XXX: see above. */ |
1593 | continue; |
1594 | |
1595 | ia6_acquire(ia, &psref); |
1596 | pserialize_read_exit(s); |
1597 | |
1598 | if (find_pfxlist_reachable_router(ia->ia6_ndpr)) { |
1599 | if (ia->ia6_flags & IN6_IFF_DETACHED) { |
1600 | ia->ia6_flags &= ~IN6_IFF_DETACHED; |
1601 | ia->ia6_flags |= IN6_IFF_TENTATIVE; |
1602 | nd6_dad_start(ifa, |
1603 | 0); |
1604 | /* We will notify the routing socket |
1605 | * of the DAD result, so no need to |
1606 | * here */ |
1607 | } |
1608 | } else { |
1609 | if ((ia->ia6_flags & IN6_IFF_DETACHED) == 0) { |
1610 | ia->ia6_flags |= IN6_IFF_DETACHED; |
1611 | rt_newaddrmsg(RTM_NEWADDR, |
1612 | ifa, 0, NULL); |
1613 | } |
1614 | } |
1615 | |
1616 | s = pserialize_read_enter(); |
1617 | ia6_release(ia, &psref); |
1618 | } |
1619 | pserialize_read_exit(s); |
1620 | curlwp_bindx(bound); |
1621 | } |
1622 | else { |
1623 | int bound = curlwp_bind(); |
1624 | |
1625 | s = pserialize_read_enter(); |
1626 | IN6_ADDRLIST_READER_FOREACH(ia) { |
1627 | if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) |
1628 | continue; |
1629 | |
1630 | if (ia->ia6_flags & IN6_IFF_DETACHED) { |
1631 | struct ifaddr *ifa = (struct ifaddr *)ia; |
1632 | struct psref psref; |
1633 | |
1634 | ia->ia6_flags &= ~IN6_IFF_DETACHED; |
1635 | ia->ia6_flags |= IN6_IFF_TENTATIVE; |
1636 | |
1637 | ia6_acquire(ia, &psref); |
1638 | pserialize_read_exit(s); |
1639 | |
1640 | /* Do we need a delay in this case? */ |
1641 | nd6_dad_start(ifa, 0); |
1642 | |
1643 | s = pserialize_read_enter(); |
1644 | ia6_release(ia, &psref); |
1645 | } |
1646 | } |
1647 | pserialize_read_exit(s); |
1648 | curlwp_bindx(bound); |
1649 | } |
1650 | } |
1651 | |
1652 | int |
1653 | nd6_prefix_onlink(struct nd_prefix *pr) |
1654 | { |
1655 | struct ifaddr *ifa; |
1656 | struct ifnet *ifp = pr->ndpr_ifp; |
1657 | struct sockaddr_in6 mask6; |
1658 | struct nd_prefix *opr; |
1659 | u_long rtflags; |
1660 | int error = 0; |
1661 | struct psref psref; |
1662 | int bound; |
1663 | |
1664 | /* sanity check */ |
1665 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { |
1666 | nd6log(LOG_ERR, "%s/%d is already on-link\n" , |
1667 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen); |
1668 | return (EEXIST); |
1669 | } |
1670 | |
1671 | /* |
1672 | * Add the interface route associated with the prefix. Before |
1673 | * installing the route, check if there's the same prefix on another |
1674 | * interface, and the prefix has already installed the interface route. |
1675 | * Although such a configuration is expected to be rare, we explicitly |
1676 | * allow it. |
1677 | */ |
1678 | LIST_FOREACH(opr, &nd_prefix, ndpr_entry) { |
1679 | if (opr == pr) |
1680 | continue; |
1681 | |
1682 | if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) |
1683 | continue; |
1684 | |
1685 | if (opr->ndpr_plen == pr->ndpr_plen && |
1686 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, |
1687 | &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) |
1688 | return (0); |
1689 | } |
1690 | |
1691 | /* |
1692 | * We prefer link-local addresses as the associated interface address. |
1693 | */ |
1694 | /* search for a link-local addr */ |
1695 | bound = curlwp_bind(); |
1696 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal_psref(ifp, |
1697 | IN6_IFF_NOTREADY | IN6_IFF_ANYCAST, &psref); |
1698 | if (ifa == NULL) { |
1699 | int s = pserialize_read_enter(); |
1700 | IFADDR_READER_FOREACH(ifa, ifp) { |
1701 | if (ifa->ifa_addr->sa_family == AF_INET6) |
1702 | break; |
1703 | } |
1704 | if (ifa != NULL) |
1705 | ifa_acquire(ifa, &psref); |
1706 | pserialize_read_exit(s); |
1707 | /* should we care about ia6_flags? */ |
1708 | } |
1709 | if (ifa == NULL) { |
1710 | /* |
1711 | * This can still happen, when, for example, we receive an RA |
1712 | * containing a prefix with the L bit set and the A bit clear, |
1713 | * after removing all IPv6 addresses on the receiving |
1714 | * interface. This should, of course, be rare though. |
1715 | */ |
1716 | nd6log(LOG_NOTICE, "failed to find any ifaddr" |
1717 | " to add route for a prefix(%s/%d) on %s\n" , |
1718 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
1719 | pr->ndpr_plen, if_name(ifp)); |
1720 | curlwp_bindx(bound); |
1721 | return (0); |
1722 | } |
1723 | |
1724 | /* |
1725 | * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. |
1726 | * ifa->ifa_rtrequest = nd6_rtrequest; |
1727 | */ |
1728 | memset(&mask6, 0, sizeof(mask6)); |
1729 | mask6.sin6_family = AF_INET6; |
1730 | mask6.sin6_len = sizeof(mask6); |
1731 | mask6.sin6_addr = pr->ndpr_mask; |
1732 | /* rtrequest() will probably set RTF_UP, but we're not sure. */ |
1733 | rtflags = ifa->ifa_flags | RTF_UP; |
1734 | if (nd6_need_cache(ifp)) { |
1735 | /* explicitly set in case ifa_flags does not set the flag. */ |
1736 | rtflags |= RTF_CONNECTED; |
1737 | } else { |
1738 | /* |
1739 | * explicitly clear the cloning bit in case ifa_flags sets it. |
1740 | */ |
1741 | rtflags &= ~RTF_CONNECTED; |
1742 | } |
1743 | error = rtrequest_newmsg(RTM_ADD, sin6tosa(&pr->ndpr_prefix), |
1744 | ifa->ifa_addr, sin6tosa(&mask6), rtflags); |
1745 | if (error == 0) { |
1746 | nd6_numroutes++; |
1747 | pr->ndpr_stateflags |= NDPRF_ONLINK; |
1748 | } else { |
1749 | nd6log(LOG_ERR, "failed to add route for a" |
1750 | " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx " |
1751 | "errno = %d\n" , |
1752 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), |
1753 | pr->ndpr_plen, if_name(ifp), |
1754 | ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), |
1755 | ip6_sprintf(&mask6.sin6_addr), rtflags, error); |
1756 | } |
1757 | ifa_release(ifa, &psref); |
1758 | curlwp_bindx(bound); |
1759 | |
1760 | return (error); |
1761 | } |
1762 | |
1763 | int |
1764 | nd6_prefix_offlink(struct nd_prefix *pr) |
1765 | { |
1766 | int error = 0; |
1767 | struct ifnet *ifp = pr->ndpr_ifp; |
1768 | struct nd_prefix *opr; |
1769 | struct sockaddr_in6 sa6, mask6; |
1770 | |
1771 | /* sanity check */ |
1772 | if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { |
1773 | nd6log(LOG_ERR, "%s/%d is already off-link\n" , |
1774 | ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen); |
1775 | return (EEXIST); |
1776 | } |
1777 | |
1778 | sockaddr_in6_init(&sa6, &pr->ndpr_prefix.sin6_addr, 0, 0, 0); |
1779 | sockaddr_in6_init(&mask6, &pr->ndpr_mask, 0, 0, 0); |
1780 | error = rtrequest_newmsg(RTM_DELETE, sin6tosa(&sa6), NULL, |
1781 | sin6tosa(&mask6), 0); |
1782 | if (error == 0) { |
1783 | pr->ndpr_stateflags &= ~NDPRF_ONLINK; |
1784 | nd6_numroutes--; |
1785 | |
1786 | /* |
1787 | * There might be the same prefix on another interface, |
1788 | * the prefix which could not be on-link just because we have |
1789 | * the interface route (see comments in nd6_prefix_onlink). |
1790 | * If there's one, try to make the prefix on-link on the |
1791 | * interface. |
1792 | */ |
1793 | LIST_FOREACH(opr, &nd_prefix, ndpr_entry) { |
1794 | if (opr == pr) |
1795 | continue; |
1796 | |
1797 | if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) |
1798 | continue; |
1799 | |
1800 | /* |
1801 | * KAME specific: detached prefixes should not be |
1802 | * on-link. |
1803 | */ |
1804 | if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) |
1805 | continue; |
1806 | |
1807 | if (opr->ndpr_plen == pr->ndpr_plen && |
1808 | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, |
1809 | &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { |
1810 | int e; |
1811 | |
1812 | if ((e = nd6_prefix_onlink(opr)) != 0) { |
1813 | nd6log(LOG_ERR, "failed to " |
1814 | "recover a prefix %s/%d from %s " |
1815 | "to %s (errno = %d)\n" , |
1816 | ip6_sprintf(&opr->ndpr_prefix.sin6_addr), |
1817 | opr->ndpr_plen, if_name(ifp), |
1818 | if_name(opr->ndpr_ifp), e); |
1819 | } |
1820 | } |
1821 | } |
1822 | } else { |
1823 | /* XXX: can we still set the NDPRF_ONLINK flag? */ |
1824 | nd6log(LOG_ERR, "failed to delete route: " |
1825 | "%s/%d on %s (errno = %d)\n" , |
1826 | ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, if_name(ifp), |
1827 | error); |
1828 | } |
1829 | |
1830 | return error; |
1831 | } |
1832 | |
1833 | static struct in6_ifaddr * |
1834 | in6_ifadd(struct nd_prefixctl *prc, int mcast, struct psref *psref) |
1835 | { |
1836 | struct ifnet *ifp = prc->ndprc_ifp; |
1837 | struct ifaddr *ifa; |
1838 | struct in6_aliasreq ifra; |
1839 | struct in6_ifaddr *ia, *ib; |
1840 | int error, plen0; |
1841 | struct in6_addr mask; |
1842 | int prefixlen = prc->ndprc_plen; |
1843 | int updateflags; |
1844 | int s; |
1845 | |
1846 | in6_prefixlen2mask(&mask, prefixlen); |
1847 | |
1848 | /* |
1849 | * find a link-local address (will be interface ID). |
1850 | * Is it really mandatory? Theoretically, a global or a site-local |
1851 | * address can be configured without a link-local address, if we |
1852 | * have a unique interface identifier... |
1853 | * |
1854 | * it is not mandatory to have a link-local address, we can generate |
1855 | * interface identifier on the fly. we do this because: |
1856 | * (1) it should be the easiest way to find interface identifier. |
1857 | * (2) RFC2462 5.4 suggesting the use of the same interface identifier |
1858 | * for multiple addresses on a single interface, and possible shortcut |
1859 | * of DAD. we omitted DAD for this reason in the past. |
1860 | * (3) a user can prevent autoconfiguration of global address |
1861 | * by removing link-local address by hand (this is partly because we |
1862 | * don't have other way to control the use of IPv6 on an interface. |
1863 | * this has been our design choice - cf. NRL's "ifconfig auto"). |
1864 | * (4) it is easier to manage when an interface has addresses |
1865 | * with the same interface identifier, than to have multiple addresses |
1866 | * with different interface identifiers. |
1867 | */ |
1868 | s = pserialize_read_enter(); |
1869 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */ |
1870 | if (ifa) |
1871 | ib = (struct in6_ifaddr *)ifa; |
1872 | else { |
1873 | pserialize_read_exit(s); |
1874 | return NULL; |
1875 | } |
1876 | |
1877 | #if 0 /* don't care link local addr state, and always do DAD */ |
1878 | /* if link-local address is not eligible, do not autoconfigure. */ |
1879 | if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) { |
1880 | printf("in6_ifadd: link-local address not ready\n" ); |
1881 | return NULL; |
1882 | } |
1883 | #endif |
1884 | |
1885 | /* prefixlen + ifidlen must be equal to 128 */ |
1886 | plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); |
1887 | if (prefixlen != plen0) { |
1888 | nd6log(LOG_INFO, "wrong prefixlen for %s " |
1889 | "(prefix=%d ifid=%d)\n" , |
1890 | if_name(ifp), prefixlen, 128 - plen0); |
1891 | pserialize_read_exit(s); |
1892 | return NULL; |
1893 | } |
1894 | |
1895 | /* make ifaddr */ |
1896 | |
1897 | memset(&ifra, 0, sizeof(ifra)); |
1898 | /* |
1899 | * in6_update_ifa() does not use ifra_name, but we accurately set it |
1900 | * for safety. |
1901 | */ |
1902 | strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); |
1903 | sockaddr_in6_init(&ifra.ifra_addr, &prc->ndprc_prefix.sin6_addr, 0, 0, 0); |
1904 | /* prefix */ |
1905 | ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; |
1906 | ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; |
1907 | ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; |
1908 | ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; |
1909 | |
1910 | /* interface ID */ |
1911 | ifra.ifra_addr.sin6_addr.s6_addr32[0] |= |
1912 | (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); |
1913 | ifra.ifra_addr.sin6_addr.s6_addr32[1] |= |
1914 | (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); |
1915 | ifra.ifra_addr.sin6_addr.s6_addr32[2] |= |
1916 | (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); |
1917 | ifra.ifra_addr.sin6_addr.s6_addr32[3] |= |
1918 | (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); |
1919 | pserialize_read_exit(s); |
1920 | |
1921 | /* new prefix mask. */ |
1922 | sockaddr_in6_init(&ifra.ifra_prefixmask, &mask, 0, 0, 0); |
1923 | |
1924 | /* lifetimes */ |
1925 | ifra.ifra_lifetime.ia6t_vltime = prc->ndprc_vltime; |
1926 | ifra.ifra_lifetime.ia6t_pltime = prc->ndprc_pltime; |
1927 | |
1928 | /* XXX: scope zone ID? */ |
1929 | |
1930 | ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ |
1931 | |
1932 | /* |
1933 | * Make sure that we do not have this address already. This should |
1934 | * usually not happen, but we can still see this case, e.g., if we |
1935 | * have manually configured the exact address to be configured. |
1936 | */ |
1937 | s = pserialize_read_enter(); |
1938 | if (in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr) != NULL) { |
1939 | /* this should be rare enough to make an explicit log */ |
1940 | log(LOG_INFO, "in6_ifadd: %s is already configured\n" , |
1941 | ip6_sprintf(&ifra.ifra_addr.sin6_addr)); |
1942 | pserialize_read_exit(s); |
1943 | return (NULL); |
1944 | } |
1945 | pserialize_read_exit(s); |
1946 | |
1947 | /* |
1948 | * Allocate ifaddr structure, link into chain, etc. |
1949 | * If we are going to create a new address upon receiving a multicasted |
1950 | * RA, we need to impose a random delay before starting DAD. |
1951 | * [draft-ietf-ipv6-rfc2462bis-02.txt, Section 5.4.2] |
1952 | */ |
1953 | updateflags = 0; |
1954 | if (mcast) |
1955 | updateflags |= IN6_IFAUPDATE_DADDELAY; |
1956 | if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0) { |
1957 | nd6log(LOG_ERR, "failed to make ifaddr %s on %s (errno=%d)\n" , |
1958 | ip6_sprintf(&ifra.ifra_addr.sin6_addr), if_name(ifp), |
1959 | error); |
1960 | return (NULL); /* ifaddr must not have been allocated. */ |
1961 | } |
1962 | |
1963 | ia = in6ifa_ifpwithaddr_psref(ifp, &ifra.ifra_addr.sin6_addr, psref); |
1964 | |
1965 | return (ia); /* this is always non-NULL */ |
1966 | } |
1967 | |
1968 | int |
1969 | in6_tmpifadd( |
1970 | const struct in6_ifaddr *ia0, /* corresponding public address */ |
1971 | int forcegen, |
1972 | int dad_delay) |
1973 | { |
1974 | struct ifnet *ifp = ia0->ia_ifa.ifa_ifp; |
1975 | struct in6_ifaddr *newia, *ia; |
1976 | struct in6_aliasreq ifra; |
1977 | int i, error; |
1978 | int trylimit = 3; /* XXX: adhoc value */ |
1979 | int updateflags; |
1980 | u_int32_t randid[2]; |
1981 | u_int32_t vltime0, pltime0; |
1982 | int s; |
1983 | |
1984 | memset(&ifra, 0, sizeof(ifra)); |
1985 | strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); |
1986 | ifra.ifra_addr = ia0->ia_addr; |
1987 | /* copy prefix mask */ |
1988 | ifra.ifra_prefixmask = ia0->ia_prefixmask; |
1989 | /* clear the old IFID */ |
1990 | for (i = 0; i < 4; i++) { |
1991 | ifra.ifra_addr.sin6_addr.s6_addr32[i] &= |
1992 | ifra.ifra_prefixmask.sin6_addr.s6_addr32[i]; |
1993 | } |
1994 | |
1995 | again: |
1996 | if (in6_get_tmpifid(ifp, (u_int8_t *)randid, |
1997 | (const u_int8_t *)&ia0->ia_addr.sin6_addr.s6_addr[8], forcegen)) { |
1998 | nd6log(LOG_NOTICE, "failed to find a good random IFID\n" ); |
1999 | return (EINVAL); |
2000 | } |
2001 | ifra.ifra_addr.sin6_addr.s6_addr32[2] |= |
2002 | (randid[0] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[2])); |
2003 | ifra.ifra_addr.sin6_addr.s6_addr32[3] |= |
2004 | (randid[1] & ~(ifra.ifra_prefixmask.sin6_addr.s6_addr32[3])); |
2005 | |
2006 | /* |
2007 | * in6_get_tmpifid() quite likely provided a unique interface ID. |
2008 | * However, we may still have a chance to see collision, because |
2009 | * there may be a time lag between generation of the ID and generation |
2010 | * of the address. So, we'll do one more sanity check. |
2011 | */ |
2012 | s = pserialize_read_enter(); |
2013 | IN6_ADDRLIST_READER_FOREACH(ia) { |
2014 | if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, |
2015 | &ifra.ifra_addr.sin6_addr)) { |
2016 | pserialize_read_exit(s); |
2017 | if (trylimit-- == 0) { |
2018 | /* |
2019 | * Give up. Something strange should have |
2020 | * happened. |
2021 | */ |
2022 | nd6log(LOG_NOTICE, |
2023 | "failed to find a unique random IFID\n" ); |
2024 | return (EEXIST); |
2025 | } |
2026 | forcegen = 1; |
2027 | goto again; |
2028 | } |
2029 | } |
2030 | pserialize_read_exit(s); |
2031 | |
2032 | /* |
2033 | * The Valid Lifetime is the lower of the Valid Lifetime of the |
2034 | * public address or TEMP_VALID_LIFETIME. |
2035 | * The Preferred Lifetime is the lower of the Preferred Lifetime |
2036 | * of the public address or TEMP_PREFERRED_LIFETIME - |
2037 | * DESYNC_FACTOR. |
2038 | */ |
2039 | if (ia0->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { |
2040 | vltime0 = IFA6_IS_INVALID(ia0) ? 0 : |
2041 | (ia0->ia6_lifetime.ia6t_vltime - |
2042 | (time_uptime - ia0->ia6_updatetime)); |
2043 | if (vltime0 > ip6_temp_valid_lifetime) |
2044 | vltime0 = ip6_temp_valid_lifetime; |
2045 | } else |
2046 | vltime0 = ip6_temp_valid_lifetime; |
2047 | if (ia0->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { |
2048 | pltime0 = IFA6_IS_DEPRECATED(ia0) ? 0 : |
2049 | (ia0->ia6_lifetime.ia6t_pltime - |
2050 | (time_uptime - ia0->ia6_updatetime)); |
2051 | if (pltime0 > ip6_temp_preferred_lifetime - ip6_desync_factor){ |
2052 | pltime0 = ip6_temp_preferred_lifetime - |
2053 | ip6_desync_factor; |
2054 | } |
2055 | } else |
2056 | pltime0 = ip6_temp_preferred_lifetime - ip6_desync_factor; |
2057 | ifra.ifra_lifetime.ia6t_vltime = vltime0; |
2058 | ifra.ifra_lifetime.ia6t_pltime = pltime0; |
2059 | |
2060 | /* |
2061 | * A temporary address is created only if this calculated Preferred |
2062 | * Lifetime is greater than REGEN_ADVANCE time units. |
2063 | */ |
2064 | if (ifra.ifra_lifetime.ia6t_pltime <= ip6_temp_regen_advance) |
2065 | return (0); |
2066 | |
2067 | /* XXX: scope zone ID? */ |
2068 | |
2069 | ifra.ifra_flags |= (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY); |
2070 | |
2071 | /* allocate ifaddr structure, link into chain, etc. */ |
2072 | updateflags = 0; |
2073 | if (dad_delay) |
2074 | updateflags |= IN6_IFAUPDATE_DADDELAY; |
2075 | if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0) |
2076 | return (error); |
2077 | |
2078 | s = pserialize_read_enter(); |
2079 | newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); |
2080 | if (newia == NULL) { /* XXX: can it happen? */ |
2081 | pserialize_read_exit(s); |
2082 | nd6log(LOG_ERR, |
2083 | "ifa update succeeded, but we got no ifaddr\n" ); |
2084 | return (EINVAL); /* XXX */ |
2085 | } |
2086 | newia->ia6_ndpr = ia0->ia6_ndpr; |
2087 | newia->ia6_ndpr->ndpr_refcnt++; |
2088 | pserialize_read_exit(s); |
2089 | |
2090 | /* |
2091 | * A newly added address might affect the status of other addresses. |
2092 | * XXX: when the temporary address is generated with a new public |
2093 | * address, the onlink check is redundant. However, it would be safe |
2094 | * to do the check explicitly everywhere a new address is generated, |
2095 | * and, in fact, we surely need the check when we create a new |
2096 | * temporary address due to deprecation of an old temporary address. |
2097 | */ |
2098 | pfxlist_onlink_check(); |
2099 | |
2100 | return (0); |
2101 | } |
2102 | |
2103 | static int |
2104 | in6_init_prefix_ltimes(struct nd_prefix *ndpr) |
2105 | { |
2106 | |
2107 | /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */ |
2108 | if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { |
2109 | nd6log(LOG_INFO, "preferred lifetime" |
2110 | "(%d) is greater than valid lifetime(%d)\n" , |
2111 | (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime); |
2112 | return (EINVAL); |
2113 | } |
2114 | if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) |
2115 | ndpr->ndpr_preferred = 0; |
2116 | else |
2117 | ndpr->ndpr_preferred = time_uptime + ndpr->ndpr_pltime; |
2118 | if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) |
2119 | ndpr->ndpr_expire = 0; |
2120 | else |
2121 | ndpr->ndpr_expire = time_uptime + ndpr->ndpr_vltime; |
2122 | |
2123 | return 0; |
2124 | } |
2125 | |
2126 | static void |
2127 | in6_init_address_ltimes(struct nd_prefix *newpr, |
2128 | struct in6_addrlifetime *lt6) |
2129 | { |
2130 | |
2131 | /* Valid lifetime must not be updated unless explicitly specified. */ |
2132 | /* init ia6t_expire */ |
2133 | if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) |
2134 | lt6->ia6t_expire = 0; |
2135 | else { |
2136 | lt6->ia6t_expire = time_uptime; |
2137 | lt6->ia6t_expire += lt6->ia6t_vltime; |
2138 | } |
2139 | |
2140 | /* init ia6t_preferred */ |
2141 | if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) |
2142 | lt6->ia6t_preferred = 0; |
2143 | else { |
2144 | lt6->ia6t_preferred = time_uptime; |
2145 | lt6->ia6t_preferred += lt6->ia6t_pltime; |
2146 | } |
2147 | } |
2148 | |
2149 | /* |
2150 | * Delete all the routing table entries that use the specified gateway. |
2151 | * XXX: this function causes search through all entries of routing table, so |
2152 | * it shouldn't be called when acting as a router. |
2153 | */ |
2154 | void |
2155 | rt6_flush(struct in6_addr *gateway, struct ifnet *ifp) |
2156 | { |
2157 | int s = splsoftnet(); |
2158 | |
2159 | /* We'll care only link-local addresses */ |
2160 | if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { |
2161 | splx(s); |
2162 | return; |
2163 | } |
2164 | |
2165 | rt_delete_matched_entries(AF_INET6, rt6_deleteroute_matcher, gateway); |
2166 | splx(s); |
2167 | } |
2168 | |
2169 | static int |
2170 | rt6_deleteroute_matcher(struct rtentry *rt, void *arg) |
2171 | { |
2172 | struct in6_addr *gate = (struct in6_addr *)arg; |
2173 | |
2174 | if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) |
2175 | return (0); |
2176 | |
2177 | if (!IN6_ARE_ADDR_EQUAL(gate, &satosin6(rt->rt_gateway)->sin6_addr)) |
2178 | return (0); |
2179 | |
2180 | /* |
2181 | * Do not delete a static route. |
2182 | * XXX: this seems to be a bit ad-hoc. Should we consider the |
2183 | * 'cloned' bit instead? |
2184 | */ |
2185 | if ((rt->rt_flags & RTF_STATIC) != 0) |
2186 | return (0); |
2187 | |
2188 | /* |
2189 | * We delete only host route. This means, in particular, we don't |
2190 | * delete default route. |
2191 | */ |
2192 | if ((rt->rt_flags & RTF_HOST) == 0) |
2193 | return (0); |
2194 | |
2195 | return 1; |
2196 | } |
2197 | |
2198 | int |
2199 | nd6_setdefaultiface(int ifindex) |
2200 | { |
2201 | ifnet_t *ifp; |
2202 | int error = 0; |
2203 | int s; |
2204 | |
2205 | s = pserialize_read_enter(); |
2206 | ifp = if_byindex(ifindex); |
2207 | if (ifp == NULL) { |
2208 | pserialize_read_exit(s); |
2209 | return EINVAL; |
2210 | } |
2211 | if (nd6_defifindex != ifindex) { |
2212 | nd6_defifindex = ifindex; |
2213 | nd6_defifp = nd6_defifindex > 0 ? ifp : NULL; |
2214 | |
2215 | /* |
2216 | * Our current implementation assumes one-to-one maping between |
2217 | * interfaces and links, so it would be natural to use the |
2218 | * default interface as the default link. |
2219 | */ |
2220 | scope6_setdefault(nd6_defifp); |
2221 | } |
2222 | pserialize_read_exit(s); |
2223 | |
2224 | return (error); |
2225 | } |
2226 | |