summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/nd6_rtr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/nd6_rtr.c')
-rw-r--r--sys/netinet6/nd6_rtr.c581
1 files changed, 470 insertions, 111 deletions
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index 2501a2d6..f403371 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1,3 +1,6 @@
+/* $FreeBSD$ */
+/* $KAME: nd6_rtr.c,v 1.43 2000/07/02 23:19:59 itojun Exp $ */
+
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
@@ -25,10 +28,11 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
@@ -47,34 +51,44 @@
#include <netinet/in.h>
#include <netinet6/in6_var.h>
-#include <netinet6/ip6.h>
+#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
-#include <netinet6/icmp6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/scope6_var.h>
#include <net/net_osdep.h>
-#define SDL(s) ((struct sockaddr_dl *)s)
+#define SDL(s) ((struct sockaddr_dl *)s)
+
+static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
+static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *));
+static struct nd_prefix *prefix_lookup __P((struct nd_prefix *));
+static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *,
+ struct in6_addr *, int));
+static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
+ struct nd_defrouter *));
+static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
+static void pfxrtr_del __P((struct nd_pfxrouter *));
+static struct nd_pfxrouter *find_pfxlist_reachable_router
+ __P((struct nd_prefix *));
+static void nd6_detach_prefix __P((struct nd_prefix *));
+static void nd6_attach_prefix __P((struct nd_prefix *));
+static void defrouter_addifreq __P((struct ifnet *));
+#ifdef ND6_USE_RTSOCK
+static void defrouter_msg __P((int, struct rtentry *));
+#endif
-static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *));
-static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *));
-static struct nd_prefix *prefix_lookup __P((struct nd_prefix *));
-static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *,
- struct in6_addr *, int));
-static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *,
- struct nd_defrouter *));
-static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *));
-static void pfxrtr_del __P((struct nd_pfxrouter *));
-static void pfxlist_onlink_check __P((void));
-static void nd6_detach_prefix __P((struct nd_prefix *));
-static void nd6_attach_prefix __P((struct nd_prefix *));
+static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
+ struct in6_addrlifetime *lt6,
+ int update_vltime));
-static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
- struct in6_addrlifetime *lt6));
+static int rt6_deleteroute __P((struct radix_node *, void *));
-static int rt6_deleteroute __P((struct radix_node *, void *));
+extern int nd6_recalc_reachtm_interval;
-extern int nd6_recalc_reachtm_interval;
+struct ifnet *nd6_defifp;
+int nd6_defifindex;
/*
* Receive Router Solicitation Message - just for routers.
@@ -90,22 +104,30 @@ nd6_rs_input(m, off, icmp6len)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
- struct nd_router_solicit *nd_rs
- = (struct nd_router_solicit *)((caddr_t)ip6 + off);
+ struct nd_router_solicit *nd_rs;
struct in6_addr saddr6 = ip6->ip6_src;
+#if 0
+ struct in6_addr daddr6 = ip6->ip6_dst;
+#endif
char *lladdr = NULL;
int lladdrlen = 0;
+#if 0
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL;
+ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL;
+ struct rtentry *rt = NULL;
+ int is_newentry;
+#endif
union nd_opts ndopts;
/* If I'm not a router, ignore it. */
if (ip6_accept_rtadv != 0 || ip6_forwarding != 1)
- return;
+ goto freeit;
/* Sanity checks */
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim);
- return;
+ goto freeit;
}
/*
@@ -113,13 +135,24 @@ nd6_rs_input(m, off, icmp6len)
* This indicates that the src has no IP address assigned yet.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
+ goto freeit;
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len);
+ if (nd_rs == NULL) {
+ icmp6stat.icp6s_tooshort++;
return;
+ }
+#endif
icmp6len -= sizeof(*nd_rs);
nd6_option_init(nd_rs + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n");
- return;
+ goto freeit;
}
if (ndopts.nd_opts_src_lladdr) {
@@ -135,6 +168,9 @@ nd6_rs_input(m, off, icmp6len)
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
+
+ freeit:
+ m_freem(m);
}
/*
@@ -152,33 +188,49 @@ nd6_ra_input(m, off, icmp6len)
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index];
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
- struct nd_router_advert *nd_ra =
- (struct nd_router_advert *)((caddr_t)ip6 + off);
+ struct nd_router_advert *nd_ra;
struct in6_addr saddr6 = ip6->ip6_src;
+#if 0
+ struct in6_addr daddr6 = ip6->ip6_dst;
+ int flags; /* = nd_ra->nd_ra_flags_reserved; */
+ int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0);
+ int is_other = ((flags & ND_RA_FLAG_OTHER) != 0);
+#endif
union nd_opts ndopts;
struct nd_defrouter *dr;
if (ip6_accept_rtadv == 0)
- return;
+ goto freeit;
if (ip6->ip6_hlim != 255) {
log(LOG_ERR,
"nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim);
- return;
+ goto freeit;
}
if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) {
log(LOG_ERR,
"nd6_ra_input: src %s is not link-local\n",
ip6_sprintf(&saddr6));
+ goto freeit;
+ }
+
+#ifndef PULLDOWN_TEST
+ IP6_EXTHDR_CHECK(m, off, icmp6len,);
+ nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off);
+#else
+ IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len);
+ if (nd_ra == NULL) {
+ icmp6stat.icp6s_tooshort++;
return;
}
+#endif
icmp6len -= sizeof(*nd_ra);
nd6_option_init(nd_ra + 1, icmp6len, &ndopts);
if (nd6_options(&ndopts) < 0) {
log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n");
- return;
+ goto freeit;
}
{
@@ -190,6 +242,9 @@ nd6_ra_input(m, off, icmp6len)
dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
dr0.expire = time_second + dr0.rtlifetime;
dr0.ifp = ifp;
+ dr0.advint = 0; /* Mobile IPv6 */
+ dr0.advint_expire = 0; /* Mobile IPv6 */
+ dr0.advints_lost = 0; /* Mobile IPv6 */
/* unspecified or not? (RFC 2461 6.3.4) */
if (advreachable) {
NTOHL(advreachable);
@@ -336,17 +391,47 @@ nd6_ra_input(m, off, icmp6len)
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0);
+
+ /*
+ * Installing a link-layer address might change the state of the
+ * router's neighbor cache, which might also affect our on-link
+ * detection of adveritsed prefixes.
+ */
+ pfxlist_onlink_check();
}
+
+freeit:
+ m_freem(m);
}
/*
* default router list proccessing sub routines
*/
+
+#ifdef ND6_USE_RTSOCK
+/* tell the change to user processes watching the routing socket. */
+static void
+defrouter_msg(cmd, rt)
+ int cmd;
+ struct rtentry *rt;
+{
+ struct rt_addrinfo info;
+
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] = rt_key(rt);
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+
+ rt_missmsg(cmd, &info, rt->rt_flags, 0);
+}
+#endif
+
void
defrouter_addreq(new)
struct nd_defrouter *new;
{
struct sockaddr_in6 def, mask, gate;
+ struct rtentry *newrt = NULL;
int s;
Bzero(&def, sizeof(def));
@@ -361,11 +446,68 @@ defrouter_addreq(new)
s = splnet();
(void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
(struct sockaddr *)&gate, (struct sockaddr *)&mask,
- RTF_GATEWAY, NULL);
+ RTF_GATEWAY, &newrt);
+ if (newrt) {
+#ifdef ND6_USE_RTSOCK
+ defrouter_msg(RTM_ADD, newrt); /* tell user process */
+#endif
+ newrt->rt_refcnt--;
+ }
splx(s);
return;
}
+/* Add a route to a given interface as default */
+void
+defrouter_addifreq(ifp)
+ struct ifnet *ifp;
+{
+ struct sockaddr_in6 def, mask;
+ struct ifaddr *ifa;
+ struct rtentry *newrt = NULL;
+ int error, flags;
+
+ bzero(&def, sizeof(def));
+ bzero(&mask, sizeof(mask));
+
+ def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = AF_INET6;
+
+ /*
+ * Search for an ifaddr beloging to the specified interface.
+ * XXX: An IPv6 address are required to be assigned on the interface.
+ */
+ if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) {
+ log(LOG_ERR, /* better error? */
+ "defrouter_addifreq: failed to find an ifaddr "
+ "to install a route to interface %s\n",
+ if_name(ifp));
+ return;
+ }
+
+ flags = ifa->ifa_flags;
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0)
+ flags &= ~RTF_CLONING;
+ if ((error = rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ ifa->ifa_addr, (struct sockaddr *)&mask,
+ flags, &newrt)) != 0) {
+ log(LOG_ERR,
+ "defrouter_addifreq: failed to install a route to "
+ "interface %s (errno = %d)\n",
+ if_name(ifp), error);
+
+ if (newrt) /* maybe unnecessary, but do it for safety */
+ newrt->rt_refcnt--;
+ } else {
+ if (newrt) {
+#ifdef ND6_USE_RTSOCK
+ defrouter_msg(RTM_ADD, newrt);
+#endif
+ newrt->rt_refcnt--;
+ }
+ }
+}
+
struct nd_defrouter *
defrouter_lookup(addr, ifp)
struct in6_addr *addr;
@@ -373,9 +515,11 @@ defrouter_lookup(addr, ifp)
{
struct nd_defrouter *dr;
- LIST_FOREACH(dr, &nd_defrouter, dr_entry)
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
return(dr);
+ }
return(NULL); /* search failed */
}
@@ -386,6 +530,7 @@ defrouter_delreq(dr, dofree)
int dofree;
{
struct sockaddr_in6 def, mask, gate;
+ struct rtentry *oldrt = NULL;
Bzero(&def, sizeof(def));
Bzero(&mask, sizeof(mask));
@@ -399,18 +544,18 @@ defrouter_delreq(dr, dofree)
rtrequest(RTM_DELETE, (struct sockaddr *)&def,
(struct sockaddr *)&gate,
(struct sockaddr *)&mask,
- RTF_GATEWAY, (struct rtentry **)0);
+ RTF_GATEWAY, &oldrt);
+ if (oldrt) {
+#ifdef ND6_USE_RTSOCK
+ defrouter_msg(RTM_DELETE, oldrt);
+#endif
+ if (oldrt->rt_refcnt <= 0)
+ oldrt->rt_refcnt++; /* XXX */
+ rtfree(oldrt);
+ }
- if (dofree)
+ if (dofree) /* XXX: necessary? */
free(dr, M_IP6NDP);
-
- if (!LIST_EMPTY(&nd_defrouter))
- defrouter_addreq(LIST_FIRST(&nd_defrouter));
-
- /*
- * xxx update the Destination Cache entries for all
- * destinations using that neighbor as a router (7.2.5)
- */
}
void
@@ -429,15 +574,15 @@ defrtrlist_del(dr)
rt6_flush(&dr->rtaddr, dr->ifp);
}
- if (dr == LIST_FIRST(&nd_defrouter))
+ if (dr == TAILQ_FIRST(&nd_defrouter))
deldr = dr; /* The router is primary. */
- LIST_REMOVE(dr, dr_entry);
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
/*
* Also delete all the pointers to the router in each prefix lists.
*/
- LIST_FOREACH(pr, &nd_prefix, ndpr_entry) {
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
struct nd_pfxrouter *pfxrtr;
if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL)
pfxrtr_del(pfxrtr);
@@ -445,14 +590,96 @@ defrtrlist_del(dr)
pfxlist_onlink_check();
/*
- * If the router is the primary one, delete the default route
- * entry in the routing table.
+ * If the router is the primary one, choose a new one.
+ * Note that defrouter_select() will remove the current gateway
+ * from the routing table.
*/
if (deldr)
- defrouter_delreq(deldr, 0);
+ defrouter_select();
+
free(dr, M_IP6NDP);
}
+/*
+ * Default Router Selection according to Section 6.3.6 of RFC 2461:
+ * 1) Routers that are reachable or probably reachable should be
+ * preferred.
+ * 2) When no routers on the list are known to be reachable or
+ * probably reachable, routers SHOULD be selected in a round-robin
+ * fashion.
+ * 3) If the Default Router List is empty, assume that all
+ * destinations are on-link.
+ */
+void
+defrouter_select()
+{
+ int s = splnet();
+ struct nd_defrouter *dr, anydr;
+ struct rtentry *rt = NULL;
+ struct llinfo_nd6 *ln = NULL;
+
+ /*
+ * Search for a (probably) reachable router from the list.
+ */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln)) {
+ /* Got it, and move it to the head */
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+ TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry);
+ break;
+ }
+ }
+
+ if ((dr = TAILQ_FIRST(&nd_defrouter))) {
+ /*
+ * De-install the previous default gateway and install
+ * a new one.
+ * Note that if there is no reachable router in the list,
+ * the head entry will be used anyway.
+ * XXX: do we have to check the current routing table entry?
+ */
+ bzero(&anydr, sizeof(anydr));
+ defrouter_delreq(&anydr, 0);
+ defrouter_addreq(dr);
+ }
+ else {
+ /*
+ * The Default Router List is empty, so install the default
+ * route to an inteface.
+ * XXX: The specification does not say this mechanism should
+ * be restricted to hosts, but this would be not useful
+ * (even harmful) for routers.
+ */
+ if (!ip6_forwarding) {
+ /*
+ * De-install the current default route
+ * in advance.
+ */
+ bzero(&anydr, sizeof(anydr));
+ defrouter_delreq(&anydr, 0);
+ if (nd6_defifp) {
+ /*
+ * Install a route to the default interface
+ * as default route.
+ */
+ defrouter_addifreq(nd6_defifp);
+ }
+#ifdef ND6_DEBUG
+ else /* noisy log? */
+ log(LOG_INFO, "defrouter_select: "
+ "there's no default router and no default"
+ " interface\n");
+#endif
+ }
+ }
+
+ splx(s);
+ return;
+}
+
static struct nd_defrouter *
defrtrlist_update(new)
struct nd_defrouter *new;
@@ -488,13 +715,15 @@ defrtrlist_update(new)
}
bzero(n, sizeof(*n));
*n = *new;
- if (LIST_EMPTY(&nd_defrouter)) {
- LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry);
- defrouter_addreq(n);
- } else {
- LIST_INSERT_AFTER(LIST_FIRST(&nd_defrouter), n, dr_entry);
- defrouter_addreq(n);
- }
+
+ /*
+ * Insert the new router at the end of the Default Router List.
+ * If there is no other router, install it anyway. Otherwise,
+ * just continue to use the current default router.
+ */
+ TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
+ if (TAILQ_FIRST(&nd_defrouter) == n)
+ defrouter_select();
splx(s);
return(n);
@@ -506,8 +735,8 @@ pfxrtr_lookup(pr, dr)
struct nd_defrouter *dr;
{
struct nd_pfxrouter *search;
-
- LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) {
+
+ for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) {
if (search->router == dr)
break;
}
@@ -547,7 +776,7 @@ prefix_lookup(pr)
{
struct nd_prefix *search;
- LIST_FOREACH(search, &nd_prefix, ndpr_entry) {
+ for (search = nd_prefix.lh_first; search; search = search->ndpr_next) {
if (pr->ndpr_ifp == search->ndpr_ifp &&
pr->ndpr_plen == search->ndpr_plen &&
in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
@@ -591,8 +820,9 @@ prelist_add(pr, dr)
LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
splx(s);
- if (dr)
+ if (dr) {
pfxrtr_add(new, dr);
+ }
return 0;
}
@@ -610,8 +840,8 @@ prelist_remove(pr)
splx(s);
/* free list of routers that adversed the prefix */
- for (pfr = LIST_FIRST(&pr->ndpr_advrtrs); pfr; pfr = next) {
- next = LIST_NEXT(pfr, pfr_entry);
+ for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) {
+ next = pfr->pfr_next;
free(pfr, M_IP6NDP);
}
@@ -638,6 +868,7 @@ prelist_update(new, dr, m)
int error = 0;
int auth;
struct in6_addrlifetime *lt6;
+ u_char onlink; /* Mobile IPv6 */
auth = 0;
if (m) {
@@ -656,6 +887,7 @@ prelist_update(new, dr, m)
error = EADDRNOTAVAIL;
goto end;
}
+
/* update prefix information */
pr->ndpr_flags = new->ndpr_flags;
pr->ndpr_vltime = new->ndpr_vltime;
@@ -707,9 +939,9 @@ prelist_update(new, dr, m)
/* address lifetime <= prefix lifetime */
lt6->ia6t_vltime = new->ndpr_vltime;
lt6->ia6t_pltime = new->ndpr_pltime;
- in6_init_address_ltimes(new, lt6);
+ in6_init_address_ltimes(new, lt6, 1);
} else {
-#define TWOHOUR (120*60)
+#define TWOHOUR (120*60)
/*
* We have seen the prefix before, and we have added
* interface address in the past. We still have
@@ -722,10 +954,26 @@ prelist_update(new, dr, m)
lt6 = &ia6->ia6_lifetime;
- /*
- * update to RFC 2462 5.5.3 (e) from Jim Bound,
- * (ipng 6712)
- */
+#if 0 /* RFC 2462 5.5.3 (e) */
+ lt6->ia6t_pltime = new->ndpr_pltime;
+ if (TWOHOUR < new->ndpr_vltime
+ || lt6pr->nd < new->ndpr_vltime) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ } else if (auth
+ && lt6->ia6t_vltime <= TWOHOUR0
+ && new->ndpr_vltime <= lt6->ia6t_vltime) {
+ lt6->ia6t_vltime = new->ndpr_vltime;
+ update++;
+ } else {
+ lt6->ia6t_vltime = TWOHOUR;
+ update++;
+ }
+
+ /* 2 hour rule is not imposed for pref lifetime */
+ new->ndpr_apltime = new->ndpr_pltime;
+ lt6->ia6t_pltime = new->ndpr_pltime;
+#else /* update from Jim Bound, (ipng 6712) */
if (TWOHOUR < new->ndpr_vltime
|| lt6->ia6t_vltime < new->ndpr_vltime) {
lt6->ia6t_vltime = new->ndpr_vltime;
@@ -737,16 +985,28 @@ prelist_update(new, dr, m)
/* jim bound rule is not imposed for pref lifetime */
lt6->ia6t_pltime = new->ndpr_pltime;
-
- update++;
- if (update)
- in6_init_address_ltimes(new, lt6);
+#endif
+ in6_init_address_ltimes(new, lt6, update);
}
noautoconf1:
+#if 0
+ /* address lifetime expire processing, RFC 2462 5.5.4. */
+ if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) {
+ struct in6_ifaddr *ia6;
+
+ ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (ia6)
+ ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
+ }
+#endif
+
+ onlink = pr->ndpr_statef_onlink; /* Mobile IPv6 */
+
if (dr && pfxrtr_lookup(pr, dr) == NULL)
pfxrtr_add(pr, dr);
+
} else {
int error_tmp;
@@ -779,7 +1039,7 @@ prelist_update(new, dr, m)
/* address lifetime <= prefix lifetime */
lt6->ia6t_vltime = new->ndpr_vltime;
lt6->ia6t_pltime = new->ndpr_pltime;
- in6_init_address_ltimes(new, lt6);
+ in6_init_address_ltimes(new, lt6, 1);
noautoconf2:
error_tmp = prelist_add(new, dr);
@@ -792,49 +1052,82 @@ prelist_update(new, dr, m)
}
/*
+ * A supplement function used in the on-link detection below;
+ * detect if a given prefix has a (probably) reachable advertising router.
+ * XXX: lengthy function name...
+ */
+struct nd_pfxrouter *
+find_pfxlist_reachable_router(pr)
+ struct nd_prefix *pr;
+{
+ struct nd_pfxrouter *pfxrtr;
+ struct rtentry *rt;
+ struct llinfo_nd6 *ln;
+
+ for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr;
+ pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) {
+ if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0,
+ pfxrtr->router->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln))
+ break; /* found */
+ }
+
+ return(pfxrtr);
+
+}
+
+/*
* Check if each prefix in the prefix list has at least one available router
- * that advertised the prefix.
- * If the check fails, the prefix may be off-link because, for example,
+ * that advertised the prefix (A router is "available" if its neighbor cache
+ * entry has reachable or probably reachable).
+ * If the check fails, the prefix may be off-link, because, for example,
* we have moved from the network but the lifetime of the prefix has not
* been expired yet. So we should not use the prefix if there is another
* prefix that has an available router.
- * But if there is no prefix that has an availble router, we still regards
+ * But if there is no prefix that has an available router, we still regards
* all the prefixes as on-link. This is because we can't tell if all the
* routers are simply dead or if we really moved from the network and there
* is no router around us.
*/
-static void
+void
pfxlist_onlink_check()
{
struct nd_prefix *pr;
- LIST_FOREACH(pr, &nd_prefix, ndpr_entry)
- if (!LIST_EMPTY(&pr->ndpr_advrtrs)) /* pr has an available router */
+ /*
+ * Check if there is a prefix that has a reachable advertising
+ * router.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (find_pfxlist_reachable_router(pr))
break;
+ }
if (pr) {
/*
- * There is at least one prefix that has a router. First,
- * detach prefixes which has no advertising router and then
- * attach other prefixes. The order is important since an
- * attached prefix and a detached prefix may have a same
- * interface route.
+ * There is at least one prefix that has a reachable router.
+ * First, detach prefixes which has no reachable advertising
+ * router and then attach other prefixes.
+ * The order is important since an attached prefix and a
+ * detached prefix may have a same interface route.
*/
- LIST_FOREACH(pr, &nd_prefix, ndpr_entry) {
- if (LIST_EMPTY(&pr->ndpr_advrtrs) &&
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (find_pfxlist_reachable_router(pr) == NULL &&
pr->ndpr_statef_onlink) {
pr->ndpr_statef_onlink = 0;
nd6_detach_prefix(pr);
}
}
- LIST_FOREACH(pr, &nd_prefix, ndpr_entry) {
- if (!LIST_EMPTY(&pr->ndpr_advrtrs) &&
- pr->ndpr_statef_onlink == 0)
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (find_pfxlist_reachable_router(pr) &&
+ pr->ndpr_statef_onlink == 0)
nd6_attach_prefix(pr);
}
- } else {
- /* there is no prefix that has a router */
- LIST_FOREACH(pr, &nd_prefix, ndpr_entry)
+ }
+ else {
+ /* there is no prefix that has a reachable router */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
if (pr->ndpr_statef_onlink == 0)
nd6_attach_prefix(pr);
}
@@ -904,7 +1197,8 @@ nd6_attach_prefix(pr)
"nd6_attach_prefix: failed to find any ifaddr"
" to add route for a prefix(%s/%d)\n",
ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen);
- } else {
+ }
+ else {
int e;
struct sockaddr_in6 mask6;
@@ -955,12 +1249,20 @@ in6_ifadd(ifp, in6, addr, prefixlen)
in6_len2mask(&mask, prefixlen);
/* find link-local address (will be interface ID) */
- ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */
if (ifa)
ib = (struct in6_ifaddr *)ifa;
else
return NULL;
+#if 0 /* don't care link local addr state, and always do DAD */
+ /* if link-local address is not eligible, do not autoconfigure. */
+ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) {
+ printf("in6_ifadd: link-local address not ready\n");
+ return NULL;
+ }
+#endif
+
/* prefixlen + ifidlen must be equal to 128 */
if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) {
log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s"
@@ -979,7 +1281,10 @@ in6_ifadd(ifp, in6, addr, prefixlen)
bzero((caddr_t)ia, sizeof(*ia));
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
- ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ if (ifp->if_flags & IFF_POINTOPOINT)
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ else
+ ia->ia_ifa.ifa_dstaddr = NULL;
ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
ia->ia_ifp = ifp;
@@ -988,14 +1293,22 @@ in6_ifadd(ifp, in6, addr, prefixlen)
for( ; oia->ia_next; oia = oia->ia_next)
continue;
oia->ia_next = ia;
- } else
+ } else {
+ /*
+ * This should be impossible, since we have at least one
+ * link-local address (see the beginning of this function).
+ * XXX: should we rather panic here?
+ */
+ printf("in6_ifadd: in6_ifaddr is NULL (impossible!)\n");
in6_ifaddr = ia;
+ }
+ /* gain a refcnt for the link from in6_ifaddr */
+ ia->ia_ifa.ifa_refcnt++;
/* link to if_addrlist */
- if (!TAILQ_EMPTY(&ifp->if_addrlist)) {
- TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia,
- ifa_list);
- }
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ /* gain another refcnt for the link from if_addrlist */
+ ia->ia_ifa.ifa_refcnt++;
/* new address */
ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
@@ -1113,6 +1426,7 @@ in6_ifdel(ifp, in6)
}
TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
+ IFAFREE(&ia->ia_ifa);
/* lladdr is never deleted */
oia = ia;
@@ -1163,16 +1477,26 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr)
}
static void
-in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
+in6_init_address_ltimes(struct nd_prefix *new,
+ struct in6_addrlifetime *lt6,
+ int update_vltime)
{
- /* init ia6t_expire */
- if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
- lt6->ia6t_expire = 0;
- else {
- lt6->ia6t_expire = time_second;
- lt6->ia6t_expire += lt6->ia6t_vltime;
+ /* Valid lifetime must not be updated unless explicitly specified. */
+ if (update_vltime) {
+ /* init ia6t_expire */
+ if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_expire = 0;
+ else {
+ lt6->ia6t_expire = time_second;
+ lt6->ia6t_expire += lt6->ia6t_vltime;
+ }
+ /* Ensure addr lifetime <= prefix lifetime. */
+ if (new->ndpr_expire && lt6->ia6t_expire &&
+ new->ndpr_expire < lt6->ia6t_expire)
+ lt6->ia6t_expire = new->ndpr_expire;
}
+
/* init ia6t_preferred */
if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME)
lt6->ia6t_preferred = 0;
@@ -1180,10 +1504,7 @@ in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
lt6->ia6t_preferred = time_second;
lt6->ia6t_preferred += lt6->ia6t_pltime;
}
- /* ensure addr lifetime <= prefix lifetime */
- if (new->ndpr_expire && lt6->ia6t_expire &&
- new->ndpr_expire < lt6->ia6t_expire)
- lt6->ia6t_expire = new->ndpr_expire;
+ /* Ensure addr lifetime <= prefix lifetime. */
if (new->ndpr_preferred && lt6->ia6t_preferred
&& new->ndpr_preferred < lt6->ia6t_preferred)
lt6->ia6t_preferred = new->ndpr_preferred;
@@ -1240,3 +1561,41 @@ rt6_deleteroute(rn, arg)
rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
#undef SIN6
}
+
+int
+nd6_setdefaultiface(ifindex)
+ int ifindex;
+{
+ int error = 0;
+
+ if (ifindex < 0 || if_index < ifindex)
+ return(EINVAL);
+
+ if (nd6_defifindex != ifindex) {
+ nd6_defifindex = ifindex;
+ if (nd6_defifindex > 0)
+ nd6_defifp = ifindex2ifnet[nd6_defifindex];
+ else
+ nd6_defifp = NULL;
+
+ /*
+ * If the Default Router List is empty, install a route
+ * to the specified interface as default or remove the default
+ * route when the default interface becomes canceled.
+ * The check for the queue is actually redundant, but
+ * we do this here to avoid re-install the default route
+ * if the list is NOT empty.
+ */
+ if (TAILQ_FIRST(&nd_defrouter) == NULL)
+ defrouter_select();
+
+ /*
+ * Our current implementation assumes one-to-one maping between
+ * interfaces and links, so it would be natural to use the
+ * default interface as the default link.
+ */
+ scope6_setdefault(nd6_defifp);
+ }
+
+ return(error);
+}
OpenPOWER on IntegriCloud