summaryrefslogtreecommitdiff
path: root/sys/netinet6/nd6_rtr.c
diff options
context:
space:
mode:
authorJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2000-02-28 11:55:24 +0000
committerJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2000-02-28 11:55:24 +0000
commit15bfd42ebaec37ba450f58b4943cb687022f54e1 (patch)
tree6f6146c185fb87733b256f354abbdc0ec3ce70a4 /sys/netinet6/nd6_rtr.c
parent6d7c6ca4e58adcc6cc1db9110fa36d1aacc6c54d (diff)
bring in recent KAME changes (only important and stable ones, as usual).
- remove net.inet6.ip6.nd6_proxyall. introduce proxy NDP code works just like "arp -s". - revise source address selection. be more careful about use of yet-to-be-valid addresses as source. - as router, transmit ICMP6_DST_UNREACH_BEYONDSCOPE against out-of-scope packet forwarding attempt. - path MTU discovery takes care of routing header properly. - be more strict about mbuf chain parsing. - nuke xxCTL_VARS #define, they are for BSDI. - disable SIOCSIFDSTADDR_IN6/SIOCSIFNETMASK_IN6 ioctl, they do not fit IPv6 model where multiple address on interface is normal. (kernel side supports them for a while for backward compat, the support will be nuked shortly) - introduce "default outgoing interface" (for spec conformance in very rare case)
Diffstat (limited to 'sys/netinet6/nd6_rtr.c')
-rw-r--r--sys/netinet6/nd6_rtr.c371
1 files changed, 267 insertions, 104 deletions
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index 868813782c9..2a1db314c7c 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1,4 +1,5 @@
-/* $OpenBSD: nd6_rtr.c,v 1.4 2000/02/07 06:09:10 itojun Exp $ */
+/* $OpenBSD: nd6_rtr.c,v 1.5 2000/02/28 11:55:23 itojun Exp $ */
+/* $KAME: nd6_rtr.c,v 1.27 2000/02/26 06:53:11 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -68,9 +69,10 @@ 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 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 *));
static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
struct in6_addrlifetime *lt6,
@@ -78,16 +80,10 @@ static void in6_init_address_ltimes __P((struct nd_prefix *ndpr,
static int rt6_deleteroute __P((struct radix_node *, void *));
-#if 0
-extern struct timeval time;
-#endif
extern int nd6_recalc_reachtm_interval;
-#if 0
-static u_char bmask [] = {
- 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe,
-};
-#endif
+struct ifnet *nd6_defifp;
+int nd6_defifindex;
/*
* Receive Router Solicitation Message - just for routers.
@@ -103,8 +99,7 @@ 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;
@@ -121,13 +116,13 @@ nd6_rs_input(m, off, icmp6len)
/* 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;
}
/*
@@ -135,13 +130,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) {
@@ -157,6 +163,9 @@ nd6_rs_input(m, off, icmp6len)
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0);
+
+ freeit:
+ m_freem(m);
}
/*
@@ -174,12 +183,11 @@ 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 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
@@ -187,26 +195,37 @@ nd6_ra_input(m, off, icmp6len)
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;
}
{
@@ -365,7 +384,17 @@ 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);
}
/*
@@ -377,15 +406,6 @@ defrouter_addreq(new)
{
struct sockaddr_in6 def, mask, gate;
int s;
-#if 0
- register struct radix_node *rn;
- register struct radix_node_head *rnh;
- struct sockaddr *ndst;
- struct ifnet *ifp = new->ifp;
- struct ifaddr *ifa;
- struct rtentry *rt;
- extern struct pool rtentry_pool;
-#endif
Bzero(&def, sizeof(def));
Bzero(&mask, sizeof(mask));
@@ -396,51 +416,52 @@ defrouter_addreq(new)
def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
gate.sin6_addr = new->rtaddr;
-#if 1
s = splnet();
-
(void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
(struct sockaddr *)&gate, (struct sockaddr *)&mask,
RTF_GATEWAY, NULL);
splx(s);
return;
-#else
- ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp);
- if (!ifa)
- return;
- if ((rnh = rt_tables[AF_INET6]) == 0)
- return;
+}
- s = splnet();
- R_Malloc(rt, struct rtentry *, sizeof(*rt));
- if (!rt)
- goto bad;
- Bzero(rt, sizeof(*rt));
- rt->rt_flags = RTF_UP | RTF_GATEWAY;
- if (rt_setgate(rt, (struct sockaddr *)&def, (struct sockaddr *)&gate)){
- Free(rt);
- goto bad;
+/* Add a route to a given interface as default */
+static void
+defrouter_addifreq(ifp)
+ struct ifnet *ifp;
+{
+ struct sockaddr_in6 def, mask;
+ struct ifaddr *ifa;
+ 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;
}
- ndst = rt_key(rt);
- Bcopy(&def, ndst, sizeof(def));
- rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)&mask,
- rnh, rt->rt_nodes);
- if (rn == 0) {
- Free(rt_key(rt));
- Free(rt);
- goto bad;
+
+ 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, NULL)) != 0) {
+ log(LOG_ERR,
+ "defrouter_addifreq: failed to install a route to "
+ "interface %s (errno = %d)\n",
+ if_name(ifp), error);
}
- ifa->ifa_refcnt++;
- rt->rt_ifa = ifa;
- rt->rt_ifp = ifp;
- rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu;
- /* xxx
- * many codes should be stolen from route.c
- */
-bad:
- splx(s);
- return;
-#endif
}
struct nd_defrouter *
@@ -450,9 +471,11 @@ defrouter_lookup(addr, ifp)
{
struct nd_defrouter *dr;
- for(dr = nd_defrouter.lh_first; dr; dr = dr->dr_next)
+ 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 */
}
@@ -478,16 +501,8 @@ defrouter_delreq(dr, dofree)
(struct sockaddr *)&mask,
RTF_GATEWAY, (struct rtentry **)0);
- if (dofree)
+ if (dofree) /* XXX: necessary? */
free(dr, M_IP6NDP);
-
- if (nd_defrouter.lh_first)
- defrouter_addreq(nd_defrouter.lh_first);
-
- /*
- * xxx update the Destination Cache entries for all
- * destinations using that neighbor as a router (7.2.5)
- */
}
void
@@ -506,10 +521,10 @@ defrtrlist_del(dr)
rt6_flush(&dr->rtaddr, dr->ifp);
}
- if (dr == nd_defrouter.lh_first)
+ 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.
@@ -522,14 +537,94 @@ 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);
+ }
+ else /* noisy log? */
+ log(LOG_INFO, "defrouter_select: "
+ "there's no default router and no default"
+ " interface\n");
+ }
+ }
+
+ splx(s);
+ return;
+}
+
static struct nd_defrouter *
defrtrlist_update(new)
struct nd_defrouter *new;
@@ -565,13 +660,15 @@ defrtrlist_update(new)
}
bzero(n, sizeof(*n));
*n = *new;
- if (nd_defrouter.lh_first == NULL) {
- LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry);
- defrouter_addreq(n);
- } else {
- LIST_INSERT_AFTER(nd_defrouter.lh_first, 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);
@@ -897,49 +994,81 @@ 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...
+ */
+static 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;
- for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next)
- if (pr->ndpr_advrtrs.lh_first) /* 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.
*/
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
- if (pr->ndpr_advrtrs.lh_first == NULL &&
+ if (find_pfxlist_reachable_router(pr) == NULL &&
pr->ndpr_statef_onlink) {
pr->ndpr_statef_onlink = 0;
nd6_detach_prefix(pr);
}
}
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
- if (pr->ndpr_advrtrs.lh_first &&
- pr->ndpr_statef_onlink == 0)
+ if (find_pfxlist_reachable_router(pr) &&
+ pr->ndpr_statef_onlink == 0)
nd6_attach_prefix(pr);
}
}
else {
- /* there is no prefix that has a router */
+ /* 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);
@@ -1062,7 +1191,7 @@ 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
@@ -1105,11 +1234,13 @@ in6_ifadd(ifp, in6, addr, prefixlen)
oia->ia_next = ia;
} else
in6_ifaddr = ia;
+ ia->ia_ifa.ifa_refcnt++;
/* link to if_addrlist */
if (ifp->if_addrlist.tqh_first != NULL) {
TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia,
ifa_list);
+ ia->ia_ifa.ifa_refcnt++;
}
#if 0
else {
@@ -1241,6 +1372,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;
@@ -1378,3 +1510,34 @@ 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();
+ }
+
+ return(error);
+}