summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoritojun <itojun@openbsd.org>2002-06-08 21:22:02 +0000
committeritojun <itojun@openbsd.org>2002-06-08 21:22:02 +0000
commitd8a7e3a701bd700a8a8835b69db2e106f957907c (patch)
tree6257fbf0d620979209b57e383f146582085a81e4
parentcomment out paragraph that does not fit to openbsd-current (diff)
downloadwireguard-openbsd-d8a7e3a701bd700a8a8835b69db2e106f957907c.tar.xz
wireguard-openbsd-d8a7e3a701bd700a8a8835b69db2e106f957907c.zip
sync with latest KAME in6_ifaddr/prefix/default router manipulation.
behavior changes: - two iocts used by ndp(8) are now obsolete (backward compat provided). use sysctl path instead. - lo0 does not get ::1 automatically. it will get ::1 when lo0 comes up.
-rw-r--r--sys/conf/files3
-rw-r--r--sys/netinet/icmp6.h10
-rw-r--r--sys/netinet/in_pcb.h11
-rw-r--r--sys/netinet6/icmp6.c5
-rw-r--r--sys/netinet6/in6.c1252
-rw-r--r--sys/netinet6/in6.h11
-rw-r--r--sys/netinet6/in6_ifattach.c545
-rw-r--r--sys/netinet6/in6_ifattach.h3
-rw-r--r--sys/netinet6/in6_prefix.c1185
-rw-r--r--sys/netinet6/in6_prefix.h91
-rw-r--r--sys/netinet6/in6_var.h42
-rw-r--r--sys/netinet6/ip6_forward.c4
-rw-r--r--sys/netinet6/ip6_input.c89
-rw-r--r--sys/netinet6/ip6_output.c4
-rw-r--r--sys/netinet6/ip6_var.h13
-rw-r--r--sys/netinet6/mld6.c73
-rw-r--r--sys/netinet6/nd6.c816
-rw-r--r--sys/netinet6/nd6.h110
-rw-r--r--sys/netinet6/nd6_nbr.c296
-rw-r--r--sys/netinet6/nd6_rtr.c1639
20 files changed, 3090 insertions, 3112 deletions
diff --git a/sys/conf/files b/sys/conf/files
index d0911d76416..b0e26ae8d7f 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.248 2002/05/16 16:16:51 provos Exp $
+# $OpenBSD: files,v 1.249 2002/06/08 21:22:02 itojun Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -859,7 +859,6 @@ file netinet6/in6_ifattach.c inet6
file netinet6/in6_cksum.c inet6
#file netinet6/in6_pcb.c inet6
file netinet6/in6_src.c inet6
-file netinet6/in6_prefix.c inet6
file netinet6/in6_proto.c inet6
file netinet6/dest6.c inet6
file netinet6/frag6.c inet6
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
index c98bc568887..a2d7000440c 100644
--- a/sys/netinet/icmp6.h
+++ b/sys/netinet/icmp6.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: icmp6.h,v 1.22 2002/05/29 02:59:12 itojun Exp $ */
-/* $KAME: icmp6.h,v 1.39 2001/02/06 03:48:06 itojun Exp $ */
+/* $OpenBSD: icmp6.h,v 1.23 2002/06/08 21:22:02 itojun Exp $ */
+/* $KAME: icmp6.h,v 1.71 2002/05/27 04:18:29 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -570,7 +570,9 @@ struct icmp6stat {
#define ICMPV6CTL_MTUDISC_HIWAT 16
#define ICMPV6CTL_MTUDISC_LOWAT 17
#define ICMPV6CTL_ND6_DEBUG 18
-#define ICMPV6CTL_MAXID 19
+#define ICMPV6CTL_ND6_DRLIST 19
+#define ICMPV6CTL_ND6_PRLIST 20
+#define ICMPV6CTL_MAXID 21
#define ICMPV6CTL_NAMES { \
{ 0, 0 }, \
@@ -592,6 +594,8 @@ struct icmp6stat {
{ "mtudisc_hiwat", CTLTYPE_INT }, \
{ "mtudisc_lowat", CTLTYPE_INT }, \
{ "nd6_debug", CTLTYPE_INT }, \
+ { 0, 0 }, \
+ { 0, 0 }, \
}
#define RTF_PROBEMTU RTF_PROTO1
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 1bea173ba59..6bca529057c 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.h,v 1.41 2002/05/31 02:39:25 angelos Exp $ */
+/* $OpenBSD: in_pcb.h,v 1.42 2002/06/08 21:22:02 itojun Exp $ */
/* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */
/*
@@ -270,13 +270,8 @@ struct rtentry *
/* INET6 stuff */
int in6_pcbnotify(struct inpcbtable *, struct sockaddr *,
- u_int, struct sockaddr *, u_int, int, void *,
- void (*)(struct inpcb *, int));
-struct in6_addr *in6_selectsrc(struct sockaddr_in6 *,
- struct ip6_pktopts *,
- struct ip6_moptions *,
- struct route_in6 *,
- struct in6_addr *, int *);
+ u_int, struct sockaddr *, u_int, int, void *,
+ void (*)(struct inpcb *, int));
int in6_selecthlim(struct inpcb *, struct ifnet *);
int in6_pcbsetport(struct in6_addr *, struct inpcb *);
#endif /* _KERNEL */
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index 39e40fdf37d..686364a29aa 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: icmp6.c,v 1.59 2002/05/31 04:27:00 itojun Exp $ */
+/* $OpenBSD: icmp6.c,v 1.60 2002/06/08 21:22:02 itojun Exp $ */
/* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */
/*
@@ -2987,6 +2987,9 @@ icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
&icmp6_mtudisc_lowat);
case ICMPV6CTL_ND6_DEBUG:
return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_debug);
+ case ICMPV6CTL_ND6_DRLIST:
+ case ICMPV6CTL_ND6_PRLIST:
+ return nd6_sysctl(name[0], oldp, oldlenp, newp, newlen);
default:
return ENOPROTOOPT;
}
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index d2bfe420ee7..fc9ac96ce99 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6.c,v 1.41 2002/06/08 00:06:58 itojun Exp $ */
+/* $OpenBSD: in6.c,v 1.42 2002/06/08 21:22:02 itojun Exp $ */
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
/*
@@ -117,6 +117,9 @@ const struct in6_addr in6mask128 = IN6MASK128;
static int in6_lifaddr_ioctl(struct socket *, u_long, caddr_t,
struct ifnet *, struct proc *);
+static int in6_ifinit(struct ifnet *, struct in6_ifaddr *,
+ struct sockaddr_in6 *, int);
+static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
const struct sockaddr_in6 sa6_any = {sizeof(sa6_any), AF_INET6,
0, 0, IN6ADDR_ANY_INIT, 0};
@@ -134,17 +137,6 @@ struct multi6_kludge {
};
/*
- * Check if the loopback entry will be automatically generated.
- * if 0 returned, will not be automatically generated.
- * if 1 returned, will be automatically generated.
- */
-static int
-in6_is_ifloop_auto(struct ifaddr *ifa)
-{
- return 0;
-}
-
-/*
* Subroutine for in6_ifaddloop() and in6_ifremloop().
* This routine does actual work.
*/
@@ -153,27 +145,33 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa)
{
struct sockaddr_in6 lo_sa;
struct sockaddr_in6 all1_sa;
- struct rtentry *nrt = NULL, **nrtp = NULL;
+ struct rtentry *nrt = NULL;
+ int e;
bzero(&lo_sa, sizeof(lo_sa));
bzero(&all1_sa, sizeof(all1_sa));
- lo_sa.sin6_family = AF_INET6;
- lo_sa.sin6_len = sizeof(struct sockaddr_in6);
- all1_sa = lo_sa;
+ lo_sa.sin6_family = all1_sa.sin6_family = AF_INET6;
+ lo_sa.sin6_len = all1_sa.sin6_len = sizeof(struct sockaddr_in6);
lo_sa.sin6_addr = in6addr_loopback;
all1_sa.sin6_addr = in6mask128;
/*
- * So we add or remove static loopback entry, here.
- * This request for deletion could fail, e.g. when we remove
- * an address right after adding it.
+ * We specify the address itself as the gateway, and set the
+ * RTF_LLINFO flag, so that the corresponding host route would have
+ * the flag, and thus applications that assume traditional behavior
+ * would be happy. Note that we assume the caller of the function
+ * (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest,
+ * which changes the outgoing interface to the loopback interface.
*/
- if (cmd == RTM_ADD)
- nrtp = &nrt;
- rtrequest(cmd, ifa->ifa_addr,
- (struct sockaddr *)&lo_sa,
- (struct sockaddr *)&all1_sa,
- RTF_UP|RTF_HOST, nrtp);
+ e = rtrequest(cmd, ifa->ifa_addr, ifa->ifa_addr,
+ (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt);
+ if (e != 0) {
+ log(LOG_ERR, "in6_ifloop_request: "
+ "%s operation failed for %s (errno=%d)\n",
+ cmd == RTM_ADD ? "ADD" : "DELETE",
+ ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr),
+ e);
+ }
/*
* Make sure rt_ifa be equal to IFA, the second argument of the
@@ -187,28 +185,47 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa)
ifa->ifa_refcnt++;
nrt->rt_ifa = ifa;
}
- if (nrt)
- nrt->rt_refcnt--;
+
+ /*
+ * Report the addition/removal of the address to the routing socket.
+ * XXX: since we called rtinit for a p2p interface with a destination,
+ * we end up reporting twice in such a case. Should we rather
+ * omit the second report?
+ */
+ if (nrt) {
+ rt_newaddrmsg(cmd, ifa, e, nrt);
+ if (cmd == RTM_DELETE) {
+ if (nrt->rt_refcnt <= 0) {
+ /* XXX: we should free the entry ourselves. */
+ nrt->rt_refcnt++;
+ rtfree(nrt);
+ }
+ } else {
+ /* the cmd must be RTM_ADD here */
+ nrt->rt_refcnt--;
+ }
+ }
}
/*
- * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link).
- * Because, KAME needs loopback rtentry for ownaddr check in
- * ip6_input().
+ * Add ownaddr as loopback rtentry. We previously add the route only if
+ * necessary (ex. on a p2p link). However, since we now manage addresses
+ * separately from prefixes, we should always add the route. We can't
+ * rely on the cloning mechanism from the corresponding interface route
+ * any more.
*/
static void
in6_ifaddloop(struct ifaddr *ifa)
{
- if (!in6_is_ifloop_auto(ifa)) {
- struct rtentry *rt;
-
- /* If there is no loopback entry, allocate one. */
- rt = rtalloc1(ifa->ifa_addr, 0);
- if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
- in6_ifloop_request(RTM_ADD, ifa);
- if (rt)
- rt->rt_refcnt--;
- }
+ struct rtentry *rt;
+
+ /* If there is no loopback entry, allocate one. */
+ rt = rtalloc1(ifa->ifa_addr, 0);
+ if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 ||
+ (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
+ in6_ifloop_request(RTM_ADD, ifa);
+ if (rt)
+ rt->rt_refcnt--;
}
/*
@@ -218,21 +235,48 @@ in6_ifaddloop(struct ifaddr *ifa)
static void
in6_ifremloop(struct ifaddr *ifa)
{
- if (!in6_is_ifloop_auto(ifa)) {
- struct in6_ifaddr *ia;
- int ia_count = 0;
-
- /* If only one ifa for the loopback entry, delete it. */
- for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
- if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa),
- &ia->ia_addr.sin6_addr)) {
- ia_count++;
- if (ia_count > 1)
- break;
- }
+ struct in6_ifaddr *ia;
+ struct rtentry *rt;
+ int ia_count = 0;
+
+ /*
+ * Some of BSD variants do not remove cloned routes
+ * from an interface direct route, when removing the direct route
+ * (see comments in net/net_osdep.h). Even for variants that do remove
+ * cloned routes, they could fail to remove the cloned routes when
+ * we handle multple addresses that share a common prefix.
+ * So, we should remove the route corresponding to the deleted address.
+ */
+
+ /*
+ * Delete the entry only if exact one ifa exists. More than one ifa
+ * can exist if we assign a same single address to multiple
+ * (probably p2p) interfaces.
+ * XXX: we should avoid such a configuration in IPv6...
+ */
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) {
+ ia_count++;
+ if (ia_count > 1)
+ break;
}
- if (ia_count == 1)
+ }
+
+ if (ia_count == 1) {
+ /*
+ * Before deleting, check if a corresponding loopbacked host
+ * route surely exists. With this check, we can avoid to
+ * delete an interface direct route whose destination is same
+ * as the address being removed. This can happen when removing
+ * a subnet-router anycast address on an interface attahced
+ * to a shared medium.
+ */
+ rt = rtalloc1(ifa->ifa_addr, 0);
+ if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 &&
+ (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
+ rt->rt_refcnt--;
in6_ifloop_request(RTM_DELETE, ifa);
+ }
}
}
@@ -261,22 +305,40 @@ in6_ifindex2scopeid(idx)
}
int
-in6_mask2len(mask)
+in6_mask2len(mask, lim0)
struct in6_addr *mask;
+ u_char *lim0;
{
- int x, y;
-
- for (x = 0; x < sizeof(*mask); x++) {
- if (mask->s6_addr8[x] != 0xff)
+ int x = 0, y;
+ u_char *lim = lim0, *p;
+
+ /* ignore the scope_id part */
+ if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask))
+ lim = (u_char *)mask + sizeof(*mask);
+ for (p = (u_char *)mask; p < lim; x++, p++) {
+ if (*p != 0xff)
break;
}
y = 0;
- if (x < sizeof(*mask)) {
+ if (p < lim) {
for (y = 0; y < 8; y++) {
- if ((mask->s6_addr8[x] & (0x80 >> y)) == 0)
+ if ((*p & (0x80 >> y)) == 0)
break;
}
}
+
+ /*
+ * when the limit pointer is given, do a stricter check on the
+ * remaining bits.
+ */
+ if (p < lim) {
+ if (y != 0 && (*p & (0x00ff >> y)) != 0)
+ return(-1);
+ for (p = p + 1; p < lim; p++)
+ if (*p != 0)
+ return(-1);
+ }
+
return x * 8 + y;
}
@@ -292,12 +354,9 @@ in6_control(so, cmd, data, ifp, p)
struct proc *p;
{
struct in6_ifreq *ifr = (struct in6_ifreq *)data;
- struct in6_ifaddr *ia, *oia;
+ struct in6_ifaddr *ia = NULL;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
struct sockaddr_in6 *sa6;
- struct sockaddr_in6 oldaddr;
- int error = 0, hostIsNew, prefixIsNew;
- int newifaddr;
time_t time_second = (time_t)time.tv_sec;
int privileged;
@@ -311,7 +370,7 @@ in6_control(so, cmd, data, ifp, p)
return (mrt6_ioctl(cmd, data));
}
- if (ifp == 0)
+ if (ifp == NULL)
return(EOPNOTSUPP);
switch (cmd) {
@@ -447,56 +506,7 @@ in6_control(so, cmd, data, ifp, p)
return(EAFNOSUPPORT);
if (!privileged)
return(EPERM);
- if (ia == NULL) {
- ia = (struct in6_ifaddr *)
- malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
- bzero((caddr_t)ia, sizeof(*ia));
- /* Initialize the address and masks */
- ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
- ia->ia_addr.sin6_family = AF_INET6;
- ia->ia_addr.sin6_len = sizeof(ia->ia_addr);
- if (ifp->if_flags & IFF_POINTOPOINT) {
- ia->ia_ifa.ifa_dstaddr
- = (struct sockaddr *)&ia->ia_dstaddr;
- ia->ia_dstaddr.sin6_family = AF_INET6;
- ia->ia_dstaddr.sin6_len = sizeof(ia->ia_dstaddr);
- } else {
- ia->ia_ifa.ifa_dstaddr = NULL;
- bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr));
- }
- ia->ia_ifa.ifa_netmask
- = (struct sockaddr *)&ia->ia_prefixmask;
- ia->ia_ifp = ifp;
- if ((oia = in6_ifaddr) != NULL) {
- for ( ; oia->ia_next; oia = oia->ia_next)
- continue;
- oia->ia_next = ia;
- } else
- in6_ifaddr = ia;
- ia->ia_ifa.ifa_refcnt++;
-
- TAILQ_INSERT_TAIL(&ifp->if_addrlist,
- (struct ifaddr *)ia, ifa_list);
- ia->ia_ifa.ifa_refcnt++;
-
- newifaddr = 1;
- } else
- newifaddr = 0;
-
- if (cmd == SIOCAIFADDR_IN6) {
- /* sanity for overflow - beware unsigned */
- struct in6_addrlifetime *lt;
- lt = &ifra->ifra_lifetime;
- if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
- && lt->ia6t_vltime + time_second < time_second) {
- return EINVAL;
- }
- if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
- && lt->ia6t_pltime + time_second < time_second) {
- return EINVAL;
- }
- }
break;
case SIOCGIFADDR_IN6:
@@ -541,6 +551,10 @@ in6_control(so, cmd, data, ifp, p)
case SIOCGIFDSTADDR_IN6:
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
return(EINVAL);
+ /*
+ * XXX: should we check if ifa_dstaddr is NULL and return
+ * an error?
+ */
ifr->ifr_dstaddr = ia->ia_dstaddr;
break;
@@ -572,6 +586,42 @@ in6_control(so, cmd, data, ifp, p)
case SIOCGIFALIFETIME_IN6:
ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
+ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+ time_t maxexpire;
+ struct in6_addrlifetime *retlt =
+ &ifr->ifr_ifru.ifru_lifetime;
+
+ /*
+ * XXX: adjust expiration time assuming time_t is
+ * signed.
+ */
+ maxexpire = (-1) &
+ ~(1 << ((sizeof(maxexpire) * 8) - 1));
+ if (ia->ia6_lifetime.ia6t_vltime <
+ maxexpire - ia->ia6_updatetime) {
+ retlt->ia6t_expire = ia->ia6_updatetime +
+ ia->ia6_lifetime.ia6t_vltime;
+ } else
+ retlt->ia6t_expire = maxexpire;
+ }
+ if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+ time_t maxexpire;
+ struct in6_addrlifetime *retlt =
+ &ifr->ifr_ifru.ifru_lifetime;
+
+ /*
+ * XXX: adjust expiration time assuming time_t is
+ * signed.
+ */
+ maxexpire = (-1) &
+ ~(1 << ((sizeof(maxexpire) * 8) - 1));
+ if (ia->ia6_lifetime.ia6t_pltime <
+ maxexpire - ia->ia6_updatetime) {
+ retlt->ia6t_preferred = ia->ia6_updatetime +
+ ia->ia6_lifetime.ia6t_pltime;
+ } else
+ retlt->ia6t_preferred = maxexpire;
+ }
break;
case SIOCSIFALIFETIME_IN6:
@@ -590,170 +640,655 @@ in6_control(so, cmd, data, ifp, p)
break;
case SIOCAIFADDR_IN6:
- prefixIsNew = 0;
- hostIsNew = 1;
+ {
+ int i, error = 0;
+ struct nd_prefix pr0, *pr;
- if (ifra->ifra_addr.sin6_len == 0) {
- ifra->ifra_addr = ia->ia_addr;
- hostIsNew = 0;
- } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr,
- &ia->ia_addr.sin6_addr))
- hostIsNew = 0;
+ /*
+ * first, make or update the interface address structure,
+ * and link it to the list.
+ */
+ if ((error = in6_update_ifa(ifp, ifra, ia)) != 0)
+ return(error);
+ if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
+ == NULL) {
+ /*
+ * this can happen when the user specify the 0 valid
+ * lifetime.
+ */
+ break;
+ }
- /* Validate address families: */
/*
- * The destination address for a p2p link must have a family
- * of AF_UNSPEC or AF_INET6.
+ * then, make the prefix on-link on the interface.
+ * XXX: we'd rather create the prefix before the address, but
+ * we need at least one address to install the corresponding
+ * interface route, so we configure the address first.
*/
- if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
- ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
- ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
- return(EAFNOSUPPORT);
+
/*
- * The prefixmask must have a family of AF_UNSPEC or AF_INET6.
+ * convert mask to prefix length (prefixmask has already
+ * been validated in in6_update_ifa().
*/
- if (ifra->ifra_prefixmask.sin6_family != AF_INET6 &&
- ifra->ifra_prefixmask.sin6_family != AF_UNSPEC)
- return(EAFNOSUPPORT);
+ bzero(&pr0, sizeof(pr0));
+ pr0.ndpr_ifp = ifp;
+ pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
+ NULL);
+ if (pr0.ndpr_plen == 128) {
+ break; /* we don't need to install a host route. */
+ }
+ pr0.ndpr_prefix = ifra->ifra_addr;
+ pr0.ndpr_mask = ifra->ifra_prefixmask.sin6_addr;
+ /* apply the mask for safety. */
+ for (i = 0; i < 4; i++) {
+ pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &=
+ ifra->ifra_prefixmask.sin6_addr.s6_addr32[i];
+ }
+ /*
+ * XXX: since we don't have an API to set prefix (not address)
+ * lifetimes, we just use the same lifetimes as addresses.
+ * The (temporarily) installed lifetimes can be overridden by
+ * later advertised RAs (when accept_rtadv is non 0), which is
+ * an intended behavior.
+ */
+ pr0.ndpr_raf_onlink = 1; /* should be configurable? */
+ pr0.ndpr_raf_auto =
+ ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0);
+ pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime;
+ pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime;
+
+ /* add the prefix if there's one. */
+ if ((pr = nd6_prefix_lookup(&pr0)) == NULL) {
+ /*
+ * nd6_prelist_add will install the corresponding
+ * interface route.
+ */
+ if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0)
+ return(error);
+ if (pr == NULL) {
+ log(LOG_ERR, "nd6_prelist_add succeeded but "
+ "no prefix\n");
+ return(EINVAL); /* XXX panic here? */
+ }
+ }
+ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) &&
+ ia->ia6_ndpr == NULL) { /* new autoconfed addr */
+ ia->ia6_ndpr = pr;
+ pr->ndpr_refcnt++;
+ }
- if (ifra->ifra_prefixmask.sin6_len) {
- in6_ifscrub(ifp, ia);
- ia->ia_prefixmask = ifra->ifra_prefixmask;
- prefixIsNew = 1;
+ /*
+ * this might affect the status of autoconfigured addresses,
+ * that is, this address might make other addresses detached.
+ */
+ pfxlist_onlink_check();
+
+ break;
+ }
+
+ case SIOCDIFADDR_IN6:
+ {
+ int i = 0, purgeprefix = 0;
+ struct nd_prefix pr0, *pr = NULL;
+
+ /*
+ * If the address being deleted is the only one that owns
+ * the corresponding prefix, expire the prefix as well.
+ * XXX: theoretically, we don't have to worry about such
+ * relationship, since we separate the address management
+ * and the prefix management. We do this, however, to provide
+ * as much backward compatibility as possible in terms of
+ * the ioctl operation.
+ */
+ bzero(&pr0, sizeof(pr0));
+ pr0.ndpr_ifp = ifp;
+ pr0.ndpr_plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr,
+ NULL);
+ if (pr0.ndpr_plen == 128)
+ goto purgeaddr;
+ pr0.ndpr_prefix = ia->ia_addr;
+ pr0.ndpr_mask = ia->ia_prefixmask.sin6_addr;
+ for (i = 0; i < 4; i++) {
+ pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &=
+ ia->ia_prefixmask.sin6_addr.s6_addr32[i];
}
- if ((ifp->if_flags & IFF_POINTOPOINT) &&
- (ifra->ifra_dstaddr.sin6_family == AF_INET6)) {
- in6_ifscrub(ifp, ia);
- oldaddr = ia->ia_dstaddr;
- ia->ia_dstaddr = ifra->ifra_dstaddr;
- /* link-local index check: should be a separate function? */
- if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
- if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) {
- /*
- * interface ID is not embedded by
- * the user
- */
- ia->ia_dstaddr.sin6_addr.s6_addr16[1]
- = htons(ifp->if_index);
- } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
- htons(ifp->if_index)) {
- ia->ia_dstaddr = oldaddr;
- return(EINVAL); /* ifid contradicts */
- }
+ /*
+ * The logic of the following condition is a bit complicated.
+ * We expire the prefix when
+ * 1. the address obeys autoconfiguration and it is the
+ * only owner of the associated prefix, or
+ * 2. the address does not obey autoconf and there is no
+ * other owner of the prefix.
+ */
+ if ((pr = nd6_prefix_lookup(&pr0)) != NULL &&
+ (((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0 &&
+ pr->ndpr_refcnt == 1) ||
+ ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0 &&
+ pr->ndpr_refcnt == 0)))
+ purgeprefix = 1;
+
+ purgeaddr:
+ in6_purgeaddr(&ia->ia_ifa);
+ if (pr && purgeprefix)
+ prelist_remove(pr);
+ break;
+ }
+
+ default:
+ if (ifp == NULL || ifp->if_ioctl == 0)
+ return(EOPNOTSUPP);
+ return((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+
+ return(0);
+}
+
+/*
+ * Update parameters of an IPv6 interface address.
+ * If necessary, a new entry is created and linked into address chains.
+ * This function is separated from in6_control().
+ * XXX: should this be performed under splnet()?
+ */
+int
+in6_update_ifa(ifp, ifra, ia)
+ struct ifnet *ifp;
+ struct in6_aliasreq *ifra;
+ struct in6_ifaddr *ia;
+{
+ int error = 0, hostIsNew = 0, plen = -1;
+ struct in6_ifaddr *oia;
+ struct sockaddr_in6 dst6;
+ struct in6_addrlifetime *lt;
+ struct in6_multi_mship *imm;
+ time_t time_second = (time_t)time.tv_sec;
+ struct rtentry *rt;
+
+ /* Validate parameters */
+ if (ifp == NULL || ifra == NULL) /* this maybe redundant */
+ return(EINVAL);
+
+ /*
+ * The destination address for a p2p link must have a family
+ * of AF_UNSPEC or AF_INET6.
+ */
+ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
+ ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
+ ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
+ return(EAFNOSUPPORT);
+ /*
+ * validate ifra_prefixmask. don't check sin6_family, netmask
+ * does not carry fields other than sin6_len.
+ */
+ if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6))
+ return(EINVAL);
+ /*
+ * Because the IPv6 address architecture is classless, we require
+ * users to specify a (non 0) prefix length (mask) for a new address.
+ * We also require the prefix (when specified) mask is valid, and thus
+ * reject a non-consecutive mask.
+ */
+ if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0)
+ return(EINVAL);
+ if (ifra->ifra_prefixmask.sin6_len != 0) {
+ plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
+ (u_char *)&ifra->ifra_prefixmask +
+ ifra->ifra_prefixmask.sin6_len);
+ if (plen <= 0)
+ return(EINVAL);
+ } else {
+ /*
+ * In this case, ia must not be NULL. We just use its prefix
+ * length.
+ */
+ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL);
+ }
+ /*
+ * If the destination address on a p2p interface is specified,
+ * and the address is a scoped one, validate/set the scope
+ * zone identifier.
+ */
+ dst6 = ifra->ifra_dstaddr;
+ if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 &&
+ (dst6.sin6_family == AF_INET6)) {
+ /* link-local index check: should be a separate function? */
+ if (IN6_IS_ADDR_LINKLOCAL(&dst6.sin6_addr)) {
+ if (dst6.sin6_addr.s6_addr16[1] == 0) {
+ /*
+ * interface ID is not embedded by
+ * the user
+ */
+ dst6.sin6_addr.s6_addr16[1] =
+ htons(ifp->if_index);
+ } else if (dst6.sin6_addr.s6_addr16[1] !=
+ htons(ifp->if_index)) {
+ return(EINVAL); /* ifid contradicts */
}
- prefixIsNew = 1; /* We lie; but effect's the same */
}
- if (hostIsNew || prefixIsNew) {
- error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0);
-#if 0
- if (error)
- goto undo;
+ }
+ /*
+ * The destination address can be specified only for a p2p or a
+ * loopback interface. If specified, the corresponding prefix length
+ * must be 128.
+ */
+ if (ifra->ifra_dstaddr.sin6_family == AF_INET6) {
+#ifdef FORCE_P2PPLEN
+ int i;
#endif
- }
- if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) {
- int error_local = 0;
+ if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) {
+ /* XXX: noisy message */
+ log(LOG_INFO, "in6_update_ifa: a destination can be "
+ "specified for a p2p or a loopback IF only\n");
+ return(EINVAL);
+ }
+ if (plen != 128) {
+ log(LOG_INFO, "in6_update_ifa: prefixlen should be "
+ "128 when dstaddr is specified\n");
+#ifdef FORCE_P2PPLEN
/*
- * join solicited multicast addr for new host id
+ * To be compatible with old configurations,
+ * such as ifconfig gif0 inet6 2001::1 2001::2
+ * prefixlen 126, we override the specified
+ * prefixmask as if the prefix length was 128.
*/
- struct in6_addr llsol;
- bzero(&llsol, sizeof(struct in6_addr));
- llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
- llsol.s6_addr32[1] = 0;
- llsol.s6_addr32[2] = htonl(1);
- llsol.s6_addr32[3] =
- ifra->ifra_addr.sin6_addr.s6_addr32[3];
- llsol.s6_addr8[12] = 0xff;
- (void)in6_addmulti(&llsol, ifp, &error_local);
- if (error == 0)
- error = error_local;
+ ifra->ifra_prefixmask.sin6_len =
+ sizeof(struct sockaddr_in6);
+ for (i = 0; i < 4; i++)
+ ifra->ifra_prefixmask.sin6_addr.s6_addr32[i] =
+ 0xffffffff;
+ plen = 128;
+#else
+ return(EINVAL);
+#endif
}
+ }
+ /* lifetime consistency check */
+ lt = &ifra->ifra_lifetime;
+ if (lt->ia6t_pltime > lt->ia6t_vltime)
+ return(EINVAL);
+ if (lt->ia6t_vltime == 0) {
+ /*
+ * the following log might be noisy, but this is a typical
+ * configuration mistake or a tool's bug.
+ */
+ log(LOG_INFO,
+ "in6_update_ifa: valid lifetime is 0 for %s\n",
+ ip6_sprintf(&ifra->ifra_addr.sin6_addr));
- ia->ia6_flags = ifra->ifra_flags;
- ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/
+ if (ia == NULL)
+ return(0); /* there's nothing to do */
+ }
- ia->ia6_lifetime = ifra->ifra_lifetime;
- /* for sanity */
- if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
- ia->ia6_lifetime.ia6t_expire =
- time_second + ia->ia6_lifetime.ia6t_vltime;
- } else
- ia->ia6_lifetime.ia6t_expire = 0;
- if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
- ia->ia6_lifetime.ia6t_preferred =
- time_second + ia->ia6_lifetime.ia6t_pltime;
+ /*
+ * If this is a new address, allocate a new ifaddr and link it
+ * into chains.
+ */
+ if (ia == NULL) {
+ hostIsNew = 1;
+ /*
+ * When in6_update_ifa() is called in a process of a received
+ * RA, it is called under an interrupt context. So, we should
+ * call malloc with M_NOWAIT.
+ */
+ ia = (struct in6_ifaddr *) malloc(sizeof(*ia), M_IFADDR,
+ M_NOWAIT);
+ if (ia == NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)ia, sizeof(*ia));
+ LIST_INIT(&ia->ia6_memberships);
+ /* Initialize the address and masks, and put time stamp */
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_addr.sin6_family = AF_INET6;
+ ia->ia_addr.sin6_len = sizeof(ia->ia_addr);
+ ia->ia6_createtime = ia->ia6_updatetime = time_second;
+ if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) {
+ /*
+ * XXX: some functions expect that ifa_dstaddr is not
+ * NULL for p2p interfaces.
+ */
+ 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;
+ if ((oia = in6_ifaddr) != NULL) {
+ for ( ; oia->ia_next; oia = oia->ia_next)
+ continue;
+ oia->ia_next = ia;
} else
- ia->ia6_lifetime.ia6t_preferred = 0;
+ in6_ifaddr = ia;
+ TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa,
+ ifa_list);
+ }
+ /* set prefix mask */
+ if (ifra->ifra_prefixmask.sin6_len) {
/*
- * Perform DAD, if needed.
+ * We prohibit changing the prefix length of an existing
+ * address, because
+ * + such an operation should be rare in IPv6, and
+ * + the operation would confuse prefix management.
*/
- if (in6if_do_dad(ifp)) {
- ia->ia6_flags |= IN6_IFF_TENTATIVE;
- nd6_dad_start((struct ifaddr *)ia, NULL);
+ if (ia->ia_prefixmask.sin6_len &&
+ in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) {
+ log(LOG_INFO, "in6_update_ifa: the prefix length of an"
+ " existing (%s) address should not be changed\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr));
+ error = EINVAL;
+ goto unlink;
}
+ ia->ia_prefixmask = ifra->ifra_prefixmask;
+ }
+
+ /*
+ * If a new destination address is specified, scrub the old one and
+ * install the new destination. Note that the interface must be
+ * p2p or loopback (see the check above.)
+ */
+ if (dst6.sin6_family == AF_INET6 &&
+ !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia->ia_dstaddr.sin6_addr)) {
+ int e;
+
+ if ((ia->ia_flags & IFA_ROUTE) != 0 &&
+ (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) != 0) {
+ log(LOG_ERR, "in6_update_ifa: failed to remove "
+ "a route to the old destination: %s\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr));
+ /* proceed anyway... */
+ } else
+ ia->ia_flags &= ~IFA_ROUTE;
+ ia->ia_dstaddr = dst6;
+ }
+
+ /*
+ * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred
+ * to see if the address is deprecated or invalidated, but initialize
+ * these members for applications.
+ */
+ ia->ia6_lifetime = ifra->ifra_lifetime;
+ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_expire =
+ time_second + ia->ia6_lifetime.ia6t_vltime;
+ } else
+ ia->ia6_lifetime.ia6t_expire = 0;
+ if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+ ia->ia6_lifetime.ia6t_preferred =
+ time_second + ia->ia6_lifetime.ia6t_pltime;
+ } else
+ ia->ia6_lifetime.ia6t_preferred = 0;
+
+ /* reset the interface and routing table appropriately. */
+ if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
+ goto unlink;
+
+ /*
+ * Make the address tentative before joining multicast addresses,
+ * so that corresponding MLD responses would not have a tentative
+ * source address.
+ */
+ ia->ia6_flags = ifra->ifra_flags;
+ ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */
+ if (hostIsNew && in6if_do_dad(ifp))
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
+
+ /*
+ * Beyond this point, we should call in6_purgeaddr upon an error,
+ * not just go to unlink.
+ */
+
+ if ((ifp->if_flags & IFF_MULTICAST) != 0) {
+ struct sockaddr_in6 mltaddr, mltmask;
+#ifndef SCOPEDROUTING
+ u_int32_t zoneid = 0;
+#endif
if (hostIsNew) {
- int iilen;
- int error_local = 0;
-
- iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) -
- in6_mask2len(&ia->ia_prefixmask.sin6_addr);
- error_local = in6_prefix_add_ifid(iilen, ia);
- if (error == 0)
- error = error_local;
+ /* join solicited multicast addr for new host id */
+ struct sockaddr_in6 llsol;
+
+ bzero(&llsol, sizeof(llsol));
+ llsol.sin6_family = AF_INET6;
+ llsol.sin6_len = sizeof(llsol);
+ llsol.sin6_addr.s6_addr16[0] = htons(0xff02);
+ llsol.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ llsol.sin6_addr.s6_addr32[1] = 0;
+ llsol.sin6_addr.s6_addr32[2] = htonl(1);
+ llsol.sin6_addr.s6_addr32[3] =
+ ifra->ifra_addr.sin6_addr.s6_addr32[3];
+ llsol.sin6_addr.s6_addr8[12] = 0xff;
+ imm = in6_joingroup(ifp, &llsol.sin6_addr, &error);
+ if (imm) {
+ LIST_INSERT_HEAD(&ia->ia6_memberships, imm,
+ i6mm_chain);
+ } else {
+ log(LOG_ERR, "in6_update_ifa: addmulti "
+ "failed for %s on %s (errno=%d)\n",
+ ip6_sprintf(&llsol.sin6_addr),
+ ifp->if_xname, error);
+ goto cleanup;
+ }
}
- return(error);
+ bzero(&mltmask, sizeof(mltmask));
+ mltmask.sin6_len = sizeof(struct sockaddr_in6);
+ mltmask.sin6_family = AF_INET6;
+ mltmask.sin6_addr = in6mask32;
- case SIOCDIFADDR_IN6:
- in6_purgeaddr(&ia->ia_ifa, ifp);
- break;
+ /*
+ * join link-local all-nodes address
+ */
+ bzero(&mltaddr, sizeof(mltaddr));
+ mltaddr.sin6_len = sizeof(struct sockaddr_in6);
+ mltaddr.sin6_family = AF_INET6;
+ mltaddr.sin6_addr = in6addr_linklocal_allnodes;
+ mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
- default:
- if (ifp == NULL || ifp->if_ioctl == 0)
- return(EOPNOTSUPP);
- return((*ifp->if_ioctl)(ifp, cmd, data));
+ /*
+ * XXX: do we really need this automatic routes?
+ * We should probably reconsider this stuff. Most applications
+ * actually do not need the routes, since they usually specify
+ * the outgoing interface.
+ */
+ rt = rtalloc1((struct sockaddr *)&mltaddr, 0);
+ if (rt) {
+ /*
+ * 32bit came from "mltmask"
+ * XXX: only works in !SCOPEDROUTING case.
+ */
+ if (memcmp(&mltaddr.sin6_addr,
+ &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
+ 32 / 8)) {
+ RTFREE(rt);
+ rt = NULL;
+ }
+ }
+ if (!rt) {
+ struct rt_addrinfo info;
+
+ bzero(&info, sizeof(info));
+ info.rti_info[RTAX_DST] = (struct sockaddr *)&mltaddr;
+ info.rti_info[RTAX_GATEWAY] =
+ (struct sockaddr *)&ia->ia_addr;
+ info.rti_info[RTAX_NETMASK] =
+ (struct sockaddr *)&mltmask;
+ info.rti_info[RTAX_IFA] =
+ (struct sockaddr *)&ia->ia_addr;
+ /* XXX: we need RTF_CLONING to fake nd6_rtrequest */
+ info.rti_flags = RTF_UP | RTF_CLONING;
+ error = rtrequest1(RTM_ADD, &info, NULL);
+ if (error)
+ goto cleanup;
+ } else {
+ RTFREE(rt);
+ }
+#ifndef SCOPEDROUTING
+ mltaddr.sin6_scope_id = zoneid; /* XXX */
+#endif
+ imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error);
+ if (imm) {
+ LIST_INSERT_HEAD(&ia->ia6_memberships, imm,
+ i6mm_chain);
+ } else {
+ log(LOG_WARNING,
+ "in6_update_ifa: addmulti failed for "
+ "%s on %s (errno=%d)\n",
+ ip6_sprintf(&mltaddr.sin6_addr),
+ ifp->if_xname, error);
+ goto cleanup;
+ }
+
+ /*
+ * join node information group address
+ */
+ if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr) == 0) {
+ imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error);
+ if (imm) {
+ LIST_INSERT_HEAD(&ia->ia6_memberships, imm,
+ i6mm_chain);
+ } else {
+ log(LOG_WARNING, "in6_update_ifa: "
+ "addmulti failed for %s on %s (errno=%d)\n",
+ ip6_sprintf(&mltaddr.sin6_addr),
+ ifp->if_xname, error);
+ /* XXX not very fatal, go on... */
+ }
+ }
+
+ if (ifp->if_flags & IFF_LOOPBACK) {
+ /*
+ * join node-local all-nodes address, on loopback.
+ * (ff01::1%ifN, and ff01::%ifN/32)
+ */
+ mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
+
+ /* XXX: again, do we really need the route? */
+ rt = rtalloc1((struct sockaddr *)&mltaddr, 0);
+ if (rt) {
+ /* 32bit came from "mltmask" */
+ if (memcmp(&mltaddr.sin6_addr,
+ &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
+ 32 / 8)) {
+ RTFREE(rt);
+ rt = NULL;
+ }
+ }
+ if (!rt) {
+ struct rt_addrinfo info;
+
+ bzero(&info, sizeof(info));
+ info.rti_info[RTAX_DST] = (struct sockaddr *)&mltaddr;
+ info.rti_info[RTAX_GATEWAY] =
+ (struct sockaddr *)&ia->ia_addr;
+ info.rti_info[RTAX_NETMASK] =
+ (struct sockaddr *)&mltmask;
+ info.rti_info[RTAX_IFA] =
+ (struct sockaddr *)&ia->ia_addr;
+ info.rti_flags = RTF_UP | RTF_CLONING;
+ error = rtrequest1(RTM_ADD, &info, NULL);
+ if (error)
+ goto cleanup;
+ } else {
+ RTFREE(rt);
+ }
+ imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error);
+ if (imm) {
+ LIST_INSERT_HEAD(&ia->ia6_memberships, imm,
+ i6mm_chain);
+ } else {
+ log(LOG_WARNING, "in6_update_ifa: "
+ "addmulti failed for %s on %s "
+ "(errno=%d)\n",
+ ip6_sprintf(&mltaddr.sin6_addr),
+ ifp->if_xname, error);
+ goto cleanup;
+ }
+ }
}
- return(0);
+ /*
+ * Perform DAD, if needed.
+ * XXX It may be of use, if we can administratively
+ * disable DAD.
+ */
+ if (hostIsNew && in6if_do_dad(ifp) &&
+ (ifra->ifra_flags & IN6_IFF_NODAD) == 0)
+ {
+ nd6_dad_start((struct ifaddr *)ia, NULL);
+ }
+
+ return(error);
+
+ unlink:
+ /*
+ * XXX: if a change of an existing address failed, keep the entry
+ * anyway.
+ */
+ if (hostIsNew)
+ in6_unlink_ifa(ia, ifp);
+ return(error);
+
+ cleanup:
+ in6_purgeaddr(&ia->ia_ifa);
+ return error;
}
void
-in6_purgeaddr(ifa, ifp)
+in6_purgeaddr(ifa)
struct ifaddr *ifa;
- struct ifnet *ifp;
{
- struct in6_ifaddr *oia, *ia = (void *) ifa;
+ struct ifnet *ifp = ifa->ifa_ifp;
+ struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa;
+ struct in6_multi_mship *imm;
/* stop DAD processing */
nd6_dad_stop(ifa);
- in6_ifscrub(ifp, ia);
+ /*
+ * delete route to the destination of the address being purged.
+ * The interface must be p2p or loopback in this case.
+ */
+ if ((ia->ia_flags & IFA_ROUTE) != 0 && ia->ia_dstaddr.sin6_len != 0) {
+ int e;
+
+ if ((e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST))
+ != 0) {
+ log(LOG_ERR, "in6_purgeaddr: failed to remove "
+ "a route to the p2p destination: %s on %s, "
+ "errno=%d\n",
+ ip6_sprintf(&ia->ia_addr.sin6_addr), ifp->if_xname,
+ e);
+ /* proceed anyway... */
+ } else
+ ia->ia_flags &= ~IFA_ROUTE;
+ }
- if (ifp->if_flags & IFF_MULTICAST) {
- /*
- * delete solicited multicast addr for deleting host id
- */
- struct in6_multi *in6m;
- struct in6_addr llsol;
- bzero(&llsol, sizeof(struct in6_addr));
- llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
- llsol.s6_addr32[1] = 0;
- llsol.s6_addr32[2] = htonl(1);
- llsol.s6_addr32[3] =
- ia->ia_addr.sin6_addr.s6_addr32[3];
- llsol.s6_addr8[12] = 0xff;
-
- IN6_LOOKUP_MULTI(llsol, ifp, in6m);
- if (in6m)
- in6_delmulti(in6m);
+ /* Remove ownaddr's loopback rtentry, if it exists. */
+ in6_ifremloop(&(ia->ia_ifa));
+
+ /*
+ * leave from multicast groups we have joined for the interface
+ */
+ while ((imm = ia->ia6_memberships.lh_first) != NULL) {
+ LIST_REMOVE(imm, i6mm_chain);
+ in6_leavegroup(imm);
}
+ in6_unlink_ifa(ia, ifp);
+}
+
+static void
+in6_unlink_ifa(ia, ifp)
+ struct in6_ifaddr *ia;
+ struct ifnet *ifp;
+{
+ struct in6_ifaddr *oia;
+ int s = splnet();
+
TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list);
- IFAFREE(&ia->ia_ifa);
oia = ia;
if (oia == (ia = in6_ifaddr))
@@ -763,20 +1298,59 @@ in6_purgeaddr(ifa, ifp)
ia = ia->ia_next;
if (ia->ia_next)
ia->ia_next = oia->ia_next;
- else
- printf("Didn't unlink in6_ifaddr from list\n");
+ else {
+ /* search failed */
+ printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n");
+ }
}
- {
- int iilen;
- iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) -
- in6_mask2len(&oia->ia_prefixmask.sin6_addr);
- in6_prefix_remove_ifid(iilen, oia);
- }
- if (oia->ia6_multiaddrs.lh_first != NULL)
+ if (oia->ia6_multiaddrs.lh_first != NULL) {
in6_savemkludge(oia);
+ }
+
+ /*
+ * When an autoconfigured address is being removed, release the
+ * reference to the base prefix. Also, since the release might
+ * affect the status of other (detached) addresses, call
+ * pfxlist_onlink_check().
+ */
+ if ((oia->ia6_flags & IN6_IFF_AUTOCONF) != 0) {
+ if (oia->ia6_ndpr == NULL) {
+ log(LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address "
+ "%p has no prefix\n", oia);
+ } else {
+ oia->ia6_ndpr->ndpr_refcnt--;
+ oia->ia6_flags &= ~IN6_IFF_AUTOCONF;
+ oia->ia6_ndpr = NULL;
+ }
+ pfxlist_onlink_check();
+ }
+
+ /*
+ * release another refcnt for the link from in6_ifaddr.
+ * Note that we should decrement the refcnt at least once for all *BSD.
+ */
IFAFREE(&oia->ia_ifa);
+
+ splx(s);
+}
+
+void
+in6_purgeif(ifp)
+ struct ifnet *ifp;
+{
+ struct ifaddr *ifa, *nifa;
+
+ for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa)
+ {
+ nifa = TAILQ_NEXT(ifa, ifa_list);
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ in6_purgeaddr(ifa);
+ }
+
+ in6_ifdetach(ifp);
}
/*
@@ -888,37 +1462,34 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p)
/* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */
bzero(&ifra, sizeof(ifra));
- bcopy(iflr->iflr_name, ifra.ifra_name,
- sizeof(ifra.ifra_name));
+ bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name));
bcopy(&iflr->addr, &ifra.ifra_addr,
- ((struct sockaddr *)&iflr->addr)->sa_len);
+ ((struct sockaddr *)&iflr->addr)->sa_len);
if (hostid) {
/* fill in hostid part */
ifra.ifra_addr.sin6_addr.s6_addr32[2] =
- hostid->s6_addr32[2];
+ hostid->s6_addr32[2];
ifra.ifra_addr.sin6_addr.s6_addr32[3] =
- hostid->s6_addr32[3];
+ hostid->s6_addr32[3];
}
if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/
bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr,
- ((struct sockaddr *)&iflr->dstaddr)->sa_len);
+ ((struct sockaddr *)&iflr->dstaddr)->sa_len);
if (hostid) {
ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] =
- hostid->s6_addr32[2];
+ hostid->s6_addr32[2];
ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] =
- hostid->s6_addr32[3];
+ hostid->s6_addr32[3];
}
}
- ifra.ifra_prefixmask.sin6_family = AF_INET6;
ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen);
ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX;
- return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra,
- ifp, p);
+ return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p);
}
case SIOCGLIFADDR:
case SIOCDLIFADDR:
@@ -948,7 +1519,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p)
} else {
if (cmd == SIOCGLIFADDR) {
/* on getting an address, take the 1st match */
- cmp = 0; /*XXX*/
+ cmp = 0; /* XXX */
} else {
/* on deleting an address, do exact match */
in6_prefixlen2mask(&mask, 128);
@@ -967,6 +1538,7 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p)
continue;
if (!cmp)
break;
+
bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate));
candidate.s6_addr32[0] &= mask.s6_addr32[0];
candidate.s6_addr32[1] &= mask.s6_addr32[1];
@@ -982,15 +1554,14 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p)
if (cmd == SIOCGLIFADDR) {
/* fill in the if_laddrreq structure */
bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len);
-
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &iflr->dstaddr,
- ia->ia_dstaddr.sin6_len);
+ ia->ia_dstaddr.sin6_len);
} else
bzero(&iflr->dstaddr, sizeof(iflr->dstaddr));
iflr->prefixlen =
- in6_mask2len(&ia->ia_prefixmask.sin6_addr);
+ in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL);
iflr->flags = ia->ia6_flags; /*XXX*/
@@ -1001,126 +1572,93 @@ in6_lifaddr_ioctl(so, cmd, data, ifp, p)
/* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */
bzero(&ifra, sizeof(ifra));
bcopy(iflr->iflr_name, ifra.ifra_name,
- sizeof(ifra.ifra_name));
+ sizeof(ifra.ifra_name));
bcopy(&ia->ia_addr, &ifra.ifra_addr,
- ia->ia_addr.sin6_len);
+ ia->ia_addr.sin6_len);
if ((ifp->if_flags & IFF_POINTOPOINT) != 0) {
bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr,
- ia->ia_dstaddr.sin6_len);
+ ia->ia_dstaddr.sin6_len);
} else {
bzero(&ifra.ifra_dstaddr,
sizeof(ifra.ifra_dstaddr));
}
bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr,
- ia->ia_prefixmask.sin6_len);
+ ia->ia_prefixmask.sin6_len);
ifra.ifra_flags = ia->ia6_flags;
-
return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra,
- ifp, p);
+ ifp, p);
}
}
}
- return EOPNOTSUPP; /*just for safety*/
-}
-
-/*
- * Delete any existing route for an interface.
- */
-void
-in6_ifscrub(ifp, ia)
- struct ifnet *ifp;
- struct in6_ifaddr *ia;
-{
- if ((ia->ia_flags & IFA_ROUTE) == 0)
- return;
- /*
- * We should check the existence of dstaddr, because link-local
- * addresses can be configured without particular destinations
- * even on point-to-point or loopback interfaces.
- * In this case, kernel would panic in rtinit()...
- */
- if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT) &&
- (ia->ia_ifa.ifa_dstaddr != NULL))
- rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
- else
- rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
- ia->ia_flags &= ~IFA_ROUTE;
-
- /* Remove ownaddr's loopback rtentry, if it exists. */
- in6_ifremloop(&(ia->ia_ifa));
+ return EOPNOTSUPP; /* just for safety */
}
/*
* Initialize an interface's intetnet6 address
* and routing table entry.
*/
-int
-in6_ifinit(ifp, ia, sin6, scrub)
+static int
+in6_ifinit(ifp, ia, sin6, newhost)
struct ifnet *ifp;
struct in6_ifaddr *ia;
struct sockaddr_in6 *sin6;
- int scrub;
+ int newhost;
{
- struct sockaddr_in6 oldaddr;
- int error, flags = RTF_UP;
+ int error = 0, plen, ifacount = 0;
int s = splimp();
+ struct ifaddr *ifa;
- oldaddr = ia->ia_addr;
- ia->ia_addr = *sin6;
/*
* Give the interface a chance to initialize
* if this is its first address,
* and to validate the address if necessary.
*/
- if (ifp->if_ioctl &&
- (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
+ for (ifa = ifp->if_addrlist.tqh_first; ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr == NULL)
+ continue; /* just for safety */
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifacount++;
+ }
+
+ ia->ia_addr = *sin6;
+
+ if (ifacount <= 1 && ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
splx(s);
- ia->ia_addr = oldaddr;
return(error);
}
+ splx(s);
- switch (ifp->if_type) {
- case IFT_ARCNET:
- case IFT_ETHER:
- case IFT_FDDI:
- ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
- ia->ia_ifa.ifa_flags |= RTF_CLONING;
- break;
- case IFT_PPP:
- ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest;
- ia->ia_ifa.ifa_flags |= RTF_CLONING;
- break;
- }
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+
+ /* we could do in(6)_socktrim here, but just omit it at this moment. */
- splx(s);
- if (scrub) {
- ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
- in6_ifscrub(ifp, ia);
- ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
- }
- /* xxx
- * in_socktrim
- */
/*
- * Add route for the network.
+ * Special case:
+ * If the destination address is specified for a point-to-point
+ * interface, install a route to the destination as an interface
+ * direct route.
*/
- ia->ia_ifa.ifa_metric = ifp->if_metric;
- if (ifp->if_flags & IFF_LOOPBACK) {
- ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
- flags |= RTF_HOST;
- } else if (ifp->if_flags & IFF_POINTOPOINT) {
- if (ia->ia_dstaddr.sin6_family != AF_INET6)
- return(0);
- flags |= RTF_HOST;
- }
- if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
+ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
+ if (plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) {
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD,
+ RTF_UP | RTF_HOST)) != 0)
+ return(error);
ia->ia_flags |= IFA_ROUTE;
+ }
- /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */
- in6_ifaddloop(&(ia->ia_ifa));
+ /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */
+ if (newhost) {
+ /* set the rtrequest function to create llinfo */
+ ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+ in6_ifaddloop(&(ia->ia_ifa));
+ }
if (ifp->if_flags & IFF_MULTICAST)
in6_restoremkludge(ia, ifp);
@@ -1312,7 +1850,7 @@ in6_addmulti(maddr6, ifp, errorp)
*errorp = ENXIO; /* XXX: appropriate? */
else
*errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI,
- (caddr_t)&ifr);
+ (caddr_t)&ifr);
if (*errorp) {
LIST_REMOVE(in6m, in6m_entry);
free(in6m, M_IPMADDR);
@@ -1512,23 +2050,6 @@ ip6_sprintf(addr)
return(ip6buf[ip6round]);
}
-int
-in6_localaddr(in6)
- struct in6_addr *in6;
-{
- struct in6_ifaddr *ia;
-
- if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6))
- return 1;
-
- for (ia = in6_ifaddr; ia; ia = ia->ia_next)
- if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
- &ia->ia_prefixmask.sin6_addr))
- return 1;
-
- return (0);
-}
-
/*
* Get a scope of the address. Node-local, link-local, site-local or global.
*/
@@ -1611,6 +2132,27 @@ in6_addr2scopeid(ifp, addr)
}
}
+int
+in6_is_addr_deprecated(sa6)
+ struct sockaddr_in6 *sa6;
+{
+ struct in6_ifaddr *ia;
+
+ for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+ if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr,
+ &sa6->sin6_addr) &&
+#ifdef SCOPEDROUTING
+ ia->ia_addr.sin6_scope_id == sa6->sin6_scope_id &&
+#endif
+ (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0)
+ return(1); /* true */
+
+ /* XXX: do we still have to go thru the rest of the list? */
+ }
+
+ return(0); /* false */
+}
+
/*
* return length of part which dst and src are equal
* hard coding...
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 42ede9a2fb3..d5958ab2598 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6.h,v 1.29 2002/06/07 15:32:42 itojun Exp $ */
+/* $OpenBSD: in6.h,v 1.30 2002/06/08 21:22:02 itojun Exp $ */
/* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */
/*
@@ -356,6 +356,15 @@ extern const struct in6_addr in6addr_linklocal_allrouters;
#define IN6_IS_SCOPE_LINKLOCAL(a) \
((IN6_IS_ADDR_LINKLOCAL(a)) || \
(IN6_IS_ADDR_MC_LINKLOCAL(a)))
+
+#define IFA6_IS_DEPRECATED(a) \
+ ((a)->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME && \
+ (u_int32_t)((time.tv_sec - (a)->ia6_updatetime)) > \
+ (a)->ia6_lifetime.ia6t_pltime)
+#define IFA6_IS_INVALID(a) \
+ ((a)->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME && \
+ (u_int32_t)((time.tv_sec - (a)->ia6_updatetime)) > \
+ (a)->ia6_lifetime.ia6t_vltime)
#endif
/*
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index 3124aaca96c..42feb0daa43 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_ifattach.c,v 1.26 2002/06/07 04:34:45 itojun Exp $ */
+/* $OpenBSD: in6_ifattach.c,v 1.27 2002/06/08 21:22:02 itojun Exp $ */
/* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */
/*
@@ -56,10 +56,11 @@
unsigned long in6_maxmtu = 0;
+int ip6_auto_linklocal = 1; /* enable by default */
+
static int get_rand_ifid(struct ifnet *, struct in6_addr *);
static int get_hw_ifid(struct ifnet *, struct in6_addr *);
static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
-static int in6_ifattach_addaddr(struct ifnet *, struct in6_ifaddr *);
static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *);
static int in6_ifattach_loopback(struct ifnet *);
@@ -152,13 +153,25 @@ found:
addr = LLADDR(sdl);
addrlen = sdl->sdl_alen;
+ switch (ifp->if_type) {
+ case IFT_IEEE1394:
+ case IFT_IEEE80211:
+ /* IEEE1394 uses 16byte length address starting with EUI64 */
+ if (addrlen > 8)
+ addrlen = 8;
+ break;
+ default:
+ break;
+ }
+
/* get EUI64 */
switch (ifp->if_type) {
+ /* IEEE802/EUI64 cases - what others? */
case IFT_ETHER:
case IFT_FDDI:
case IFT_ATM:
- /* IEEE802/EUI64 cases - what others? */
-
+ case IFT_IEEE1394:
+ case IFT_IEEE80211:
/* look at IEEE802/EUI64 only */
if (addrlen != 8 && addrlen != 6)
return -1;
@@ -305,189 +318,135 @@ success:
return 0;
}
-/*
- * configure IPv6 interface address. XXX code duplicated with in.c
- */
static int
-in6_ifattach_addaddr(ifp, ia)
+in6_ifattach_linklocal(ifp, altifp)
struct ifnet *ifp;
- struct in6_ifaddr *ia;
+ struct ifnet *altifp; /*secondary EUI64 source*/
{
- struct in6_ifaddr *oia;
- struct ifaddr *ifa;
- int error;
- int rtflag;
- struct in6_addr llsol;
-
- /*
- * initialize if_addrlist, if we are the very first one
- */
- ifa = TAILQ_FIRST(&ifp->if_addrlist);
- if (ifa == NULL) {
- TAILQ_INIT(&ifp->if_addrlist);
- }
-
- /*
- * link the interface address to global list
- */
- TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
- ia->ia_ifa.ifa_refcnt++;
+ struct in6_ifaddr *ia;
+ struct in6_aliasreq ifra;
+ struct nd_prefix pr0;
+ int i, error;
/*
- * Also link into the IPv6 address chain beginning with in6_ifaddr.
- * kazu opposed it, but itojun & jinmei wanted.
+ * configure link-local address.
*/
- if ((oia = in6_ifaddr) != NULL) {
- for (; oia->ia_next; oia = oia->ia_next)
- continue;
- oia->ia_next = ia;
- } else
- in6_ifaddr = ia;
- ia->ia_ifa.ifa_refcnt++;
+ bzero(&ifra, sizeof(ifra));
/*
- * give the interface a chance to initialize, in case this
- * is the first address to be added.
+ * in6_update_ifa() does not use ifra_name, but we accurately set it
+ * for safety.
*/
- if (ifp->if_ioctl != NULL) {
- int s;
- s = splimp();
- error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
- splx(s);
- } else
- error = 0;
- if (error) {
- switch (error) {
- case EAFNOSUPPORT:
- printf("%s: IPv6 not supported\n", ifp->if_xname);
- break;
- default:
- printf("%s: SIOCSIFADDR error %d\n", ifp->if_xname,
- error);
- break;
+ strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name));
+
+ ifra.ifra_addr.sin6_family = AF_INET6;
+ ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
+ ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ ifra.ifra_addr.sin6_addr.s6_addr32[1] = 0;
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] = 0;
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1);
+ } else {
+ if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) {
+ nd6log((LOG_ERR,
+ "%s: no ifid available\n", ifp->if_xname));
+ return(-1);
}
-
- /* undo changes */
- TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
- IFAFREE(&ia->ia_ifa);
- if (oia)
- oia->ia_next = ia->ia_next;
- else
- in6_ifaddr = ia->ia_next;
- IFAFREE(&ia->ia_ifa);
- return -1;
}
- /* configure link-layer address resolution */
- rtflag = 0;
- if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128))
- rtflag = RTF_HOST;
- else {
- switch (ifp->if_type) {
- case IFT_LOOP:
-#ifdef IFT_STF
- case IFT_STF:
+ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_prefixmask.sin6_family = AF_INET6;
+ ifra.ifra_prefixmask.sin6_addr = in6mask64;
+#ifdef SCOPEDROUTING
+ /* take into account the sin6_scope_id field for routing */
+ ifra.ifra_prefixmask.sin6_scope_id = 0xffffffff;
#endif
- rtflag = 0;
- break;
- default:
- ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
- ia->ia_ifa.ifa_flags |= RTF_CLONING;
- rtflag = RTF_CLONING;
- break;
- }
- }
-
- /* add route to the interface. */
- rtrequest(RTM_ADD,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&ia->ia_prefixmask,
- RTF_UP | rtflag,
- (struct rtentry **)0);
- ia->ia_flags |= IFA_ROUTE;
+ /* link-local addresses should NEVER expire. */
+ ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
- if ((rtflag & RTF_CLONING) != 0 &&
- (ifp->if_flags & IFF_MULTICAST) != 0) {
- /* Restore saved multicast addresses (if any). */
- in6_restoremkludge(ia, ifp);
+ /*
+ * Do not let in6_update_ifa() do DAD, since we need a random delay
+ * before sending an NS at the first time the interface becomes up.
+ * Instead, in6_if_up() will start DAD with a proper random delay.
+ */
+ ifra.ifra_flags |= IN6_IFF_NODAD;
+ /*
+ * Now call in6_update_ifa() to do a bunch of procedures to configure
+ * a link-local address. We can set NULL to the 3rd argument, because
+ * we know there's no other link-local address on the interface
+ * and therefore we are adding one (instead of updating one).
+ */
+ if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
/*
- * join solicited multicast address
+ * XXX: When the interface does not support IPv6, this call
+ * would fail in the SIOCSIFADDR ioctl. I believe the
+ * notification is rather confusing in this case, so just
+ * suppress it. (jinmei@kame.net 20010130)
*/
- bzero(&llsol, sizeof(llsol));
- llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
- llsol.s6_addr32[1] = 0;
- llsol.s6_addr32[2] = htonl(1);
- llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
- llsol.s6_addr8[12] = 0xff;
- (void)in6_addmulti(&llsol, ifp, &error);
-
- /* XXX should we run DAD on other interface types? */
- if (in6if_do_dad(ifp)) {
- /* mark the address TENTATIVE, if needed. */
- ia->ia6_flags |= IN6_IFF_TENTATIVE;
- /* nd6_dad_start() will be called in in6_if_up */
- }
+ if (error != EAFNOSUPPORT)
+ log(LOG_NOTICE, "in6_ifattach_linklocal: failed to "
+ "configure a link-local address on %s "
+ "(errno=%d)\n",
+ ifp->if_xname, error);
+ return(-1);
}
- return 0;
-}
-
-static int
-in6_ifattach_linklocal(ifp, altifp)
- struct ifnet *ifp;
- struct ifnet *altifp; /*secondary EUI64 source*/
-{
- struct in6_ifaddr *ia;
-
/*
- * configure link-local address
+ * Adjust ia6_flags so that in6_if_up will perform DAD.
+ * XXX: Some P2P interfaces seem not to send packets just after
+ * becoming up, so we skip p2p interfaces for safety.
*/
- ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
- bzero((caddr_t)ia, sizeof(*ia));
- ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
- 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;
-
- bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
- ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_prefixmask.sin6_family = AF_INET6;
- ia->ia_prefixmask.sin6_addr = in6mask64;
-
- /* just in case */
- bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr));
- ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_dstaddr.sin6_family = AF_INET6;
-
- bzero(&ia->ia_addr, sizeof(ia->ia_addr));
- ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_addr.sin6_family = AF_INET6;
- ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
- ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
- ia->ia_addr.sin6_addr.s6_addr32[1] = 0;
- if (ifp->if_flags & IFF_LOOPBACK) {
- ia->ia_addr.sin6_addr.s6_addr32[2] = 0;
- ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1);
- } else {
- if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) {
- nd6log((LOG_ERR,
- "%s: no ifid available\n", ifp->if_xname));
- free(ia, M_IFADDR);
- return -1;
- }
+ ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */
+#ifdef DIAGNOSTIC
+ if (!ia) {
+ panic("ia == NULL in in6_ifattach_linklocal");
+ /* NOTREACHED */
+ }
+#endif
+ if (in6if_do_dad(ifp) && (ifp->if_flags & IFF_POINTOPOINT) == 0) {
+ ia->ia6_flags &= ~IN6_IFF_NODAD;
+ ia->ia6_flags |= IN6_IFF_TENTATIVE;
}
- ia->ia_ifa.ifa_metric = ifp->if_metric;
-
- if (in6_ifattach_addaddr(ifp, ia) != 0) {
- /* ia will be freed on failure */
- return -1;
+ /*
+ * Make the link-local prefix (fe80::/64%link) as on-link.
+ * Since we'd like to manage prefixes separately from addresses,
+ * we make an ND6 prefix structure for the link-local prefix,
+ * and add it to the prefix list as a never-expire prefix.
+ * XXX: this change might affect some existing code base...
+ */
+ bzero(&pr0, sizeof(pr0));
+ pr0.ndpr_ifp = ifp;
+ /* this should be 64 at this moment. */
+ pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL);
+ pr0.ndpr_mask = ifra.ifra_prefixmask.sin6_addr;
+ pr0.ndpr_prefix = ifra.ifra_addr;
+ /* apply the mask for safety. (nd6_prelist_add will apply it again) */
+ for (i = 0; i < 4; i++) {
+ pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &=
+ in6mask64.s6_addr32[i];
+ }
+ /*
+ * Initialize parameters. The link-local prefix must always be
+ * on-link, and its lifetimes never expire.
+ */
+ pr0.ndpr_raf_onlink = 1;
+ pr0.ndpr_raf_auto = 1; /* probably meaningless */
+ pr0.ndpr_vltime = ND6_INFINITE_LIFETIME;
+ pr0.ndpr_pltime = ND6_INFINITE_LIFETIME;
+ /*
+ * Since there is no other link-local addresses, nd6_prefix_lookup()
+ * probably returns NULL. However, we cannot always expect the result.
+ * For example, if we first remove the (only) existing link-local
+ * address, and then reconfigure another one, the prefix is still
+ * valid with referring to the old link-local address.
+ */
+ if (nd6_prefix_lookup(&pr0) == NULL) {
+ if ((error = nd6_prelist_add(&pr0, NULL, NULL)) != 0)
+ return(error);
}
return 0;
@@ -497,49 +456,106 @@ static int
in6_ifattach_loopback(ifp)
struct ifnet *ifp; /* must be IFT_LOOP */
{
- struct in6_ifaddr *ia;
+ struct in6_aliasreq ifra;
+ int error;
+
+ bzero(&ifra, sizeof(ifra));
/*
- * configure link-local address
+ * in6_update_ifa() does not use ifra_name, but we accurately set it
+ * for safety.
*/
- ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK);
- 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;
- ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask;
- ia->ia_ifp = ifp;
-
- bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
- ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_prefixmask.sin6_family = AF_INET6;
- ia->ia_prefixmask.sin6_addr = in6mask128;
+ strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name));
+
+ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_prefixmask.sin6_family = AF_INET6;
+ ifra.ifra_prefixmask.sin6_addr = in6mask128;
/*
* Always initialize ia_dstaddr (= broadcast address) to loopback
- * address, to make getifaddr happier.
- *
- * For BSDI, it is mandatory. The BSDI version of
- * ifa_ifwithroute() rejects to add a route to the loopback
- * interface. Even for other systems, loopback looks somewhat
- * special.
+ * address. Follows IPv4 practice - see in_ifinit().
*/
- bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr));
- ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_dstaddr.sin6_family = AF_INET6;
- ia->ia_dstaddr.sin6_addr = in6addr_loopback;
+ ifra.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_dstaddr.sin6_family = AF_INET6;
+ ifra.ifra_dstaddr.sin6_addr = in6addr_loopback;
- bzero(&ia->ia_addr, sizeof(ia->ia_addr));
- ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_addr.sin6_family = AF_INET6;
- ia->ia_addr.sin6_addr = in6addr_loopback;
+ ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_addr.sin6_family = AF_INET6;
+ ifra.ifra_addr.sin6_addr = in6addr_loopback;
- ia->ia_ifa.ifa_metric = ifp->if_metric;
+ /* the loopback address should NEVER expire. */
+ ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ /* we don't need to perform DAD on loopback interfaces. */
+ ifra.ifra_flags |= IN6_IFF_NODAD;
+
+ /*
+ * We are sure that this is a newly assigned address, so we can set
+ * NULL to the 3rd arg.
+ */
+ if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
+ log(LOG_ERR, "in6_ifattach_loopback: failed to configure "
+ "the loopback address on %s (errno=%d)\n",
+ ifp->if_xname, error);
+ return(-1);
+ }
+
+ return 0;
+}
- if (in6_ifattach_addaddr(ifp, ia) != 0) {
- /* ia will be freed on failure */
+/*
+ * compute NI group address, based on the current hostname setting.
+ * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later).
+ *
+ * when ifp == NULL, the caller is responsible for filling scopeid.
+ */
+int
+in6_nigroup(ifp, name, namelen, sa6)
+ struct ifnet *ifp;
+ const char *name;
+ int namelen;
+ struct sockaddr_in6 *sa6;
+{
+ const char *p;
+ u_char *q;
+ MD5_CTX ctxt;
+ u_int8_t digest[16];
+ char l;
+ char n[64]; /* a single label must not exceed 63 chars */
+
+ if (!namelen || !name)
return -1;
+
+ p = name;
+ while (p && *p && *p != '.' && p - name < namelen)
+ p++;
+ if (p - name > sizeof(n) - 1)
+ return -1; /* label too long */
+ l = p - name;
+ strncpy(n, name, l);
+ n[(int)l] = '\0';
+ for (q = n; *q; q++) {
+ if ('A' <= *q && *q <= 'Z')
+ *q = *q - 'A' + 'a';
}
+ /* generate 8 bytes of pseudo-random value. */
+ bzero(&ctxt, sizeof(ctxt));
+ MD5Init(&ctxt);
+ MD5Update(&ctxt, &l, sizeof(l));
+ MD5Update(&ctxt, n, l);
+ MD5Final(digest, &ctxt);
+
+ bzero(sa6, sizeof(*sa6));
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_len = sizeof(*sa6);
+ sa6->sin6_addr.s6_addr16[0] = htons(0xff02);
+ sa6->sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ sa6->sin6_addr.s6_addr8[11] = 2;
+ bcopy(digest, &sa6->sin6_addr.s6_addr32[3],
+ sizeof(sa6->sin6_addr.s6_addr32[3]));
+
return 0;
}
@@ -553,10 +569,6 @@ in6_ifattach(ifp, altifp)
struct ifnet *ifp;
struct ifnet *altifp; /* secondary EUI64 source */
{
- struct sockaddr_in6 mltaddr;
- struct sockaddr_in6 mltmask;
- struct sockaddr_in6 gate;
- struct sockaddr_in6 mask;
struct in6_ifaddr *ia;
struct in6_addr in6;
@@ -614,114 +626,27 @@ in6_ifattach(ifp, altifp)
}
/*
- * assign link-local address, if there's none
- */
- ia = in6ifa_ifpforlinklocal(ifp, 0);
- if (ia == NULL) {
- if (in6_ifattach_linklocal(ifp, altifp) != 0)
- return;
- ia = in6ifa_ifpforlinklocal(ifp, 0);
-
- if (ia == NULL) {
- printf("%s: failed to add link-local address\n",
- ifp->if_xname);
-
- /* we can't initialize multicasts without link-local */
- return;
- }
- }
-
- if (ifp->if_flags & IFF_POINTOPOINT) {
- /*
- * route local address to loopback
- */
- bzero(&gate, sizeof(gate));
- gate.sin6_len = sizeof(struct sockaddr_in6);
- gate.sin6_family = AF_INET6;
- gate.sin6_addr = in6addr_loopback;
- bzero(&mask, sizeof(mask));
- mask.sin6_len = sizeof(struct sockaddr_in6);
- mask.sin6_family = AF_INET6;
- mask.sin6_addr = in6mask64;
- rtrequest(RTM_ADD,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&gate,
- (struct sockaddr *)&mask,
- RTF_UP|RTF_HOST,
- (struct rtentry **)0);
- }
-
- /*
* assign loopback address for loopback interface
* XXX multiple loopback interface case
*/
- in6 = in6addr_loopback;
- if (ifp->if_flags & IFF_LOOPBACK) {
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ in6 = in6addr_loopback;
if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) {
if (in6_ifattach_loopback(ifp) != 0)
return;
}
}
-#ifdef DIAGNOSTIC
- if (!ia) {
- panic("ia == NULL in in6_ifattach");
- /*NOTREACHED*/
- }
-#endif
-
/*
- * join multicast
+ * assign a link-local address, if there's none.
*/
- if (ifp->if_flags & IFF_MULTICAST) {
- int error; /* not used */
- struct in6_multi *in6m;
-
- /* Restore saved multicast addresses(if any). */
- in6_restoremkludge(ia, ifp);
-
- bzero(&mltmask, sizeof(mltmask));
- mltmask.sin6_len = sizeof(struct sockaddr_in6);
- mltmask.sin6_family = AF_INET6;
- mltmask.sin6_addr = in6mask32;
-
- /*
- * join link-local all-nodes address
- */
- bzero(&mltaddr, sizeof(mltaddr));
- mltaddr.sin6_len = sizeof(struct sockaddr_in6);
- mltaddr.sin6_family = AF_INET6;
- mltaddr.sin6_addr = in6addr_linklocal_allnodes;
- mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-
- IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
- if (in6m == NULL) {
- rtrequest(RTM_ADD,
- (struct sockaddr *)&mltaddr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&mltmask,
- RTF_UP|RTF_CLONING, /* xxx */
- (struct rtentry **)0);
- (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
- }
-
- if (ifp->if_flags & IFF_LOOPBACK) {
- in6 = in6addr_loopback;
- ia = in6ifa_ifpwithaddr(ifp, &in6);
- /*
- * join node-local all-nodes address, on loopback
- */
- mltaddr.sin6_addr = in6addr_nodelocal_allnodes;
-
- IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m);
- if (in6m == NULL && ia != NULL) {
- rtrequest(RTM_ADD,
- (struct sockaddr *)&mltaddr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&mltmask,
- RTF_UP,
- (struct rtentry **)0);
- (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error);
+ if (ip6_auto_linklocal) {
+ ia = in6ifa_ifpforlinklocal(ifp, 0);
+ if (ia == NULL) {
+ if (in6_ifattach_linklocal(ifp, altifp) == 0) {
+ /* linklocal address assigned */
+ } else {
+ /* failed to assign linklocal address. bark? */
}
}
}
@@ -739,10 +664,7 @@ in6_ifdetach(ifp)
struct rtentry *rt;
short rtflags;
struct sockaddr_in6 sin6;
- struct in6_multi *in6m;
-
- /* nuke prefix list. this may try to remove some of ifaddrs as well */
- in6_purgeprefix(ifp);
+ struct in6_multi_mship *imm;
/* remove neighbor management table */
nd6_purge(ifp);
@@ -753,7 +675,7 @@ in6_ifdetach(ifp)
next = ifa->ifa_list.tqe_next;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
- in6_purgeaddr(ifa, ifp);
+ in6_purgeaddr(ifa);
}
/* undo everything done by in6_ifattach(), just in case */
@@ -766,20 +688,23 @@ in6_ifdetach(ifp)
ia = (struct in6_ifaddr *)ifa;
- /* leave from all multicast groups joined */
- while ((in6m = LIST_FIRST(&ia->ia6_multiaddrs)) != NULL)
- in6_delmulti(in6m);
+ /*
+ * leave from multicast groups we have joined for the interface
+ */
+ while ((imm = ia->ia6_memberships.lh_first) != NULL) {
+ LIST_REMOVE(imm, i6mm_chain);
+ in6_leavegroup(imm);
+ }
/* remove from the routing table */
- if ((ia->ia_flags & IFA_ROUTE)
- && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) {
+ if ((ia->ia_flags & IFA_ROUTE) &&
+ (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0))) {
rtflags = rt->rt_flags;
rtfree(rt);
- rtrequest(RTM_DELETE,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)&ia->ia_prefixmask,
- rtflags, (struct rtentry **)0);
+ rtrequest(RTM_DELETE, (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_addr,
+ (struct sockaddr *)&ia->ia_prefixmask,
+ rtflags, (struct rtentry **)0);
}
/* remove from the linked list */
diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h
index b04dda6a4f0..b567235e921 100644
--- a/sys/netinet6/in6_ifattach.h
+++ b/sys/netinet6/in6_ifattach.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_ifattach.h,v 1.3 2002/03/14 01:27:12 millert Exp $ */
+/* $OpenBSD: in6_ifattach.h,v 1.4 2002/06/08 21:22:02 itojun Exp $ */
/* $KAME: in6_ifattach.h,v 1.9 2000/04/12 05:35:48 itojun Exp $ */
/*
@@ -36,6 +36,7 @@
#ifdef _KERNEL
void in6_ifattach(struct ifnet *, struct ifnet *);
void in6_ifdetach(struct ifnet *);
+int in6_nigroup(struct ifnet *, const char *, int, struct sockaddr_in6 *);
#endif /* _KERNEL */
#endif /* _NETINET6_IN6_IFATTACH_H_ */
diff --git a/sys/netinet6/in6_prefix.c b/sys/netinet6/in6_prefix.c
deleted file mode 100644
index 94f36028733..00000000000
--- a/sys/netinet6/in6_prefix.c
+++ /dev/null
@@ -1,1185 +0,0 @@
-/* $OpenBSD: in6_prefix.c,v 1.13 2002/03/14 01:27:12 millert Exp $ */
-/* $KAME: in6_prefix.c,v 1.47 2001/03/25 08:41:39 itojun Exp $ */
-
-/*
- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * 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.
- */
-
-/*
- * Copyright (c) 1982, 1986, 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * 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.
- *
- * @(#)in.c 8.2 (Berkeley) 11/15/93
- */
-
-#include <sys/param.h>
-#include <sys/ioctl.h>
-#include <sys/malloc.h>
-#include <sys/kernel.h>
-#include <sys/socket.h>
-#include <sys/socketvar.h>
-#include <sys/sockio.h>
-#include <sys/systm.h>
-#include <sys/syslog.h>
-#include <sys/proc.h>
-
-#include <net/if.h>
-
-#include <netinet/in.h>
-#include <netinet/in_var.h>
-#include <netinet/ip6.h>
-#include <netinet6/in6_prefix.h>
-#include <netinet6/ip6_var.h>
-
-struct rr_prhead rr_prefix;
-
-struct timeout in6_rr_timer_ch;
-
-static void add_each_addr(struct socket *so, struct rr_prefix *rpp,
- struct rp_addr *rap);
-static int create_ra_entry(struct rp_addr **rapp);
-static int add_each_prefix(struct socket *so, struct rr_prefix *rpp);
-static void free_rp_entries(struct rr_prefix *rpp);
-static int link_stray_ia6s(struct rr_prefix *rpp);
-static void rp_remove(struct rr_prefix *rpp);
-
-/*
- * Copy bits from src to tgt, from off bit for len bits.
- * Caller must specify collect tgtsize and srcsize.
- */
-static void
-bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize,
- u_int off, u_int len)
-{
- char *sp, *tp;
-
- /* arg values check */
- if (srcsize < off || srcsize < (off + len) ||
- tgtsize < off || tgtsize < (off + len)) {
- log(LOG_ERR,
- "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n"
- "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off,
- len);
- return;
- }
-
- /* search start point */
- for (sp = src, tp = tgt; off >= 8; sp++, tp++)
- off-=8;
- /* copy starting bits */
- if (off) {
- char setbit;
- int startbits;
-
- startbits = min((8 - off), len);
-
- for (setbit = (0x80 >> off); startbits;
- setbit >>= 1, startbits--, len--)
- *tp |= (setbit & *sp);
- tp++;
- sp++;
- }
- /* copy midium bits */
- for (; len >= 8; sp++, tp++) {
- *tp = *sp;
- len-=8;
- }
- /* copy ending bits */
- if (len) {
- char setbit;
-
- for (setbit = 0x80; len; setbit >>= 1, len--)
- *tp |= (setbit & *sp);
- }
-}
-
-static struct ifprefix *
-in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst)
-{
- struct ifprefix *ifpr;
-
- /* search matched prefix */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
- {
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr)))
- break;
- }
- return (ifpr);
-}
-
-/*
- * Search prefix which matches arg prefix as specified in
- * draft-ietf-ipngwg-router-renum-08.txt
- */
-static struct rr_prefix *
-search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr)
-{
- struct ifprefix *ifpr;
- struct ifaddr *ifa;
- struct rr_prefix *rpp;
-
- /* search matched prefix */
- ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen,
- &ipr->ipr_prefix.sin6_addr);
- if (ifpr != NULL)
- return ifpr2rp(ifpr);
-
- /*
- * search matched addr, and then search prefix
- * which matches the addr
- */
-
- for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
- {
- if (ifa->ifa_addr->sa_family != AF_INET6)
- continue;
- if (ipr->ipr_plen <=
- in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa)))
- break;
- }
- if (ifa == NULL)
- return NULL;
-
- rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
- if (rpp != 0)
- return rpp;
-
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
- {
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa),
- IFPR_IN6(ifpr)))
- break;
- }
- if (ifpr != NULL)
- log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s"
- "has no pointer to prefix %s\n", ip6_sprintf(IFA_IN6(ifa)),
- ip6_sprintf(IFPR_IN6(ifpr)));
- return ifpr2rp(ifpr);
-}
-
-/*
- * Search prefix which matches arg prefix as specified in
- * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists.
- * Return 1 if anything matched, and 0 if nothing matched.
- */
-static int
-mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr)
-{
- struct ifprefix *ifpr;
- struct ifaddr *ifa;
- int matchlen, matched = 0;
-
- /* search matched prefixes */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
- {
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
- IFPR_IN6(ifpr));
- if (irr->irr_m_minlen > ifpr->ifpr_plen ||
- irr->irr_m_maxlen < ifpr->ifpr_plen ||
- irr->irr_m_len > matchlen)
- continue;
- matched = 1;
- ifpr2rp(ifpr)->rp_statef_addmark = 1;
- if (cmd == SIOCCIFPREFIX_IN6)
- ifpr2rp(ifpr)->rp_statef_delmark = 1;
- }
-
- /*
- * search matched addr, and then search prefixes
- * which matche the addr
- */
- for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
- {
- struct rr_prefix *rpp;
-
- if (ifa->ifa_addr->sa_family != AF_INET6)
- continue;
- matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr,
- IFA_IN6(ifa));
- if (irr->irr_m_minlen > matchlen ||
- irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen)
- continue;
- rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
- if (rpp != 0) {
- matched = 1;
- rpp->rp_statef_addmark = 1;
- if (cmd == SIOCCIFPREFIX_IN6)
- rpp->rp_statef_delmark = 1;
- } else
- log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:"
- "no back pointer to ifprefix for %s. "
- "ND autoconfigured addr?\n",
- ip6_sprintf(IFA_IN6(ifa)));
- }
- return matched;
-}
-
-/*
- * Mark global prefixes as to be deleted.
- */
-static void
-delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr)
-{
- struct ifprefix *ifpr;
-
- /* search matched prefixes */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
- {
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- /* mark delete global prefix */
- if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) ==
- IPV6_ADDR_SCOPE_GLOBAL)
- ifpr2rp(ifpr)->rp_statef_delmark = 1;
- }
-}
-
-/* Unmark prefixes */
-static void
-unmark_prefixes(struct ifnet *ifp)
-{
- struct ifprefix *ifpr;
-
- /* unmark all prefix */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
- {
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- /* unmark prefix */
- ifpr2rp(ifpr)->rp_statef_addmark = 0;
- ifpr2rp(ifpr)->rp_statef_delmark = 0;
- }
-}
-
-static void
-init_prefix_ltimes(struct rr_prefix *rpp)
-{
- long time_second = time.tv_sec;
-
- if (rpp->rp_pltime == RR_INFINITE_LIFETIME ||
- rpp->rp_rrf_decrprefd == 0)
- rpp->rp_preferred = 0;
- else
- rpp->rp_preferred = time_second + rpp->rp_pltime;
- if (rpp->rp_vltime == RR_INFINITE_LIFETIME ||
- rpp->rp_rrf_decrvalid == 0)
- rpp->rp_expire = 0;
- else
- rpp->rp_expire = time_second + rpp->rp_vltime;
-}
-
-static int
-rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len)
-{
- int ii_bytelen, ii_bitlen;
- int p_bytelen, p_bitlen;
-
- /* sanity check */
- if (1 > ii_len ||
- ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */
- log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n",
- ii_len);
- return(0);
- }
-
- ii_bytelen = ii_len / 8;
- ii_bitlen = ii_len % 8;
-
- p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1;
- p_bitlen = 8 - ii_bitlen;
-
- if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1,
- ii_bytelen))
- return(0);
- if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) !=
- ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff))
- return(0);
-
- return(1);
-}
-
-static struct rp_addr *
-search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid)
-{
- struct rp_addr *rap;
-
- for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
- rap = rap->ra_entry.le_next)
- {
- if (rr_are_ifid_equal(ifid, &rap->ra_ifid,
- (sizeof(struct in6_addr) << 3) -
- rpp->rp_plen))
- break;
- }
- return rap;
-}
-
-static int
-assign_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia)
-{
- int error = 0;
- struct rp_addr *rap;
- int s;
-
- if ((error = create_ra_entry(&rap)) != 0)
- return error;
-
- /* copy interface id part */
- bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
- (caddr_t)IA6_IN6(ia),
- sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen);
- /* link to ia, and put into list */
- rap->ra_addr = ia;
- rap->ra_addr->ia_ifa.ifa_refcnt++;
-#if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */
- ia->ia6_ifpr = rp2ifpr(rpp);
-#endif
- s = splnet();
- LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
- splx(s);
-
- return 0;
-}
-
-/*
- * add a link-local address to an interface. we will add new interface address
- * (prefix database + new interface id).
- */
-static int
-in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia)
-{
- struct rr_prefix *rpp;
- struct rp_addr *rap;
- struct socket so;
- int error, s;
-
- if ((error = create_ra_entry(&rap)) != 0)
- return(error);
- /* copy interface id part */
- bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
- (caddr_t)IA6_IN6(ia), sizeof(*IA6_IN6(ia)) << 3,
- 64, (sizeof(rap->ra_ifid) << 3) - 64);
- /* XXX: init dummy so */
- bzero(&so, sizeof(so));
- /* insert into list */
- for (rpp = LIST_FIRST(&rr_prefix); rpp; rpp = LIST_NEXT(rpp, rp_entry))
- {
- /*
- * do not attempt to add an address, if ifp does not match
- */
- if (rpp->rp_ifp != ia->ia_ifp)
- continue;
-
- s = splnet();
- LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
- splx(s);
- add_each_addr(&so, rpp, rap);
- }
- return 0;
-}
-
-/*
- * add an address to an interface. if the interface id portion is new,
- * we will add new interface address (prefix database + new interface id).
- */
-int
-in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia)
-{
- int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen;
- struct ifprefix *ifpr;
- struct rp_addr *rap;
- int error = 0;
-
- if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia)))
- return(in6_prefix_add_llifid(iilen, ia));
- ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia));
- if (ifpr == NULL) {
- struct rr_prefix rp;
- struct socket so;
- int pplen = (plen == 128) ? 64 : plen;
-
- /* allocate a prefix for ia, with default properties */
-
- /* init rp */
- bzero(&rp, sizeof(rp));
- rp.rp_type = IN6_PREFIX_RR;
- rp.rp_ifp = ia->ia_ifp;
- rp.rp_plen = pplen;
- rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix);
- rp.rp_prefix.sin6_family = AF_INET6;
- bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3,
- (char *)&ia->ia_addr.sin6_addr,
- sizeof(ia->ia_addr.sin6_addr) << 3,
- 0, pplen);
- rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME;
- rp.rp_raf_onlink = 1;
- rp.rp_raf_auto = 1;
- /* Is some FlagMasks for rrf necessary? */
- rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0;
- rp.rp_origin = PR_ORIG_RR; /* can be renumbered */
-
- /* create ra_entry */
- error = link_stray_ia6s(&rp);
- if (error != 0) {
- free_rp_entries(&rp);
- return error;
- }
-
- /* XXX: init dummy so */
- bzero(&so, sizeof(so));
- so.so_state |= SS_PRIV;
-
- error = add_each_prefix(&so, &rp);
-
- /* free each rp_addr entry */
- free_rp_entries(&rp);
-
- if (error != 0)
- return error;
-
- /* search again */
- ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia));
- if (ifpr == NULL)
- return 0;
- }
- rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia));
- if (rap != NULL) {
- if (rap->ra_addr == NULL) {
- rap->ra_addr = ia;
- rap->ra_addr->ia_ifa.ifa_refcnt++;
- } else if (rap->ra_addr != ia) {
- /* There may be some inconsistencies between addrs. */
- log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix"
- " already has another ia %p(%s) on its ifid list\n",
- ip6_sprintf(IA6_IN6(ia)), plen,
- rap->ra_addr,
- ip6_sprintf(IA6_IN6(rap->ra_addr)));
- return EADDRINUSE /* XXX */;
- }
- ia->ia6_ifpr = ifpr;
- return 0;
- }
- error = assign_ra_entry(ifpr2rp(ifpr), iilen, ia);
- if (error == 0)
- ia->ia6_ifpr = ifpr;
- return (error);
-}
-
-void
-in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia)
-{
- struct rp_addr *rap;
-
- if (ia->ia6_ifpr == NULL)
- return;
- rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia));
- if (rap != NULL) {
- int s = splnet();
- LIST_REMOVE(rap, ra_entry);
- splx(s);
- if (rap->ra_addr)
- IFAFREE(&rap->ra_addr->ia_ifa);
- free(rap, M_RR_ADDR);
- }
- if (LIST_FIRST(&ifpr2rp(ia->ia6_ifpr)->rp_addrhead) == NULL)
- rp_remove(ifpr2rp(ia->ia6_ifpr));
-}
-
-void
-in6_purgeprefix(ifp)
- struct ifnet *ifp;
-{
- struct ifprefix *ifpr, *nextifpr;
-
- /* delete prefixes before ifnet goes away */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr)
- {
- nextifpr = ifpr->ifpr_next;
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- (void)delete_each_prefix(ifpr2rp(ifpr), PR_ORIG_KERNEL);
- }
-}
-
-static void
-add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)
-{
- struct in6_ifaddr *ia6;
- struct in6_aliasreq ifra;
- int error;
-
- /* init ifra */
- bzero(&ifra, sizeof(ifra));
- strncpy(ifra.ifra_name, rpp->rp_ifp->if_xname, sizeof(ifra.ifra_name));
- ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family =
- AF_INET6;
- ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len =
- sizeof(ifra.ifra_addr);
- /* copy prefix part */
- bit_copy((char *)&ifra.ifra_addr.sin6_addr,
- sizeof(ifra.ifra_addr.sin6_addr) << 3,
- (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
- 0, rpp->rp_plen);
- /* copy interface id part */
- bit_copy((char *)&ifra.ifra_addr.sin6_addr,
- sizeof(ifra.ifra_addr.sin6_addr) << 3,
- (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3,
- rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen);
- in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen);
- /* don't care ifra_flags for now */
-
- ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr);
- if (ia6 != NULL) {
- if (ia6->ia6_ifpr == NULL) {
- /* link this addr and the prefix each other */
- if (rap->ra_addr)
- IFAFREE(&rap->ra_addr->ia_ifa);
- rap->ra_addr = ia6;
- rap->ra_addr->ia_ifa.ifa_refcnt++;
- ia6->ia6_ifpr = rp2ifpr(rpp);
- return;
- }
- if (ia6->ia6_ifpr == rp2ifpr(rpp)) {
- if (rap->ra_addr)
- IFAFREE(&rap->ra_addr->ia_ifa);
- rap->ra_addr = ia6;
- rap->ra_addr->ia_ifa.ifa_refcnt++;
- return;
- }
- /*
- * The addr is already assigned to other
- * prefix.
- * There may be some inconsistencies between
- * prefixes.
- * e.g. overraped prefixes with common starting
- * part and different plefixlen.
- * Or, completely duplicated prefixes?
- * log it and return.
- */
- log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr "
- "%s/%d failed because there is already another addr %s/%d\n",
- ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
- ip6_sprintf(IA6_IN6(ia6)),
- in6_mask2len(&ia6->ia_prefixmask.sin6_addr));
- return;
- }
- /* propagate ANYCAST flag if it is set for ancestor addr */
- if (rap->ra_flags.anycast != 0)
- ifra.ifra_flags |= IN6_IFF_ANYCAST;
- error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp
- , curproc);
- if (error != 0) {
- log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr"
- "%s/%d failed because in6_control failed for error %d\n",
- ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen,
- error);
- return;
- }
-
- /*
- * link beween this addr and the prefix will be done
- * in in6_prefix_add_ifid
- */
-}
-
-static int
-rrpr_update(struct socket *so, struct rr_prefix *new)
-{
- struct rr_prefix *rpp;
- struct ifprefix *ifpr;
- struct rp_addr *rap;
- int s;
-
- /* search existing prefix */
- for (ifpr = new->rp_ifp->if_prefixlist; ifpr; ifpr = ifpr->ifpr_next)
- {
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- if (ifpr->ifpr_plen == new->rp_plen &&
- in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new),
- ifpr->ifpr_plen))
- break;
- }
- rpp = ifpr2rp(ifpr);
- if (rpp != NULL) {
- /*
- * We got a prefix which we have seen in the past.
- */
- /*
- * If the origin of the already-installed prefix is more
- * preferable than the new one, ignore installation request.
- */
- if (rpp->rp_origin > new->rp_origin)
- return(EPERM);
-
- /* update prefix information */
- rpp->rp_flags.prf_ra = new->rp_flags.prf_ra;
- if (rpp->rp_origin >= PR_ORIG_RR)
- rpp->rp_flags.prf_rr = new->rp_flags.prf_rr;
- rpp->rp_vltime = new->rp_vltime;
- rpp->rp_pltime = new->rp_pltime;
- rpp->rp_expire = new->rp_expire;
- rpp->rp_preferred = new->rp_preferred;
- rpp->rp_statef_delmark = 0; /* cancel deletion */
- /*
- * Interface id related update.
- * add rp_addr entries in new into rpp, if they have not
- * been already included in rpp.
- */
- while (new->rp_addrhead.lh_first != NULL)
- {
- rap = LIST_FIRST(&new->rp_addrhead);
- LIST_REMOVE(rap, ra_entry);
- if (search_ifidwithprefix(rpp, &rap->ra_ifid)
- != NULL) {
- if (rap->ra_addr)
- IFAFREE(&rap->ra_addr->ia_ifa);
- free(rap, M_RR_ADDR);
- continue;
- }
- s = splnet();
- LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
- splx(s);
- }
- } else {
- /*
- * We got a fresh prefix.
- */
- /* create new prefix */
- rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR,
- M_NOWAIT);
- if (rpp == NULL) {
- log(LOG_ERR, "in6_prefix.c: rrpr_update:%d"
- ": ENOBUFS for rr_prefix\n", __LINE__);
- return(ENOBUFS);
- }
- /* initilization */
- *rpp = *new;
- LIST_INIT(&rpp->rp_addrhead);
- /* move rp_addr entries of new to rpp */
- while (new->rp_addrhead.lh_first != NULL)
- {
- rap = LIST_FIRST(&new->rp_addrhead);
- LIST_REMOVE(rap, ra_entry);
- LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
- }
-
- /* let rp_ifpr.ifpr_prefix point rr_prefix. */
- rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix;
- /* link rr_prefix entry to if_prefixlist */
- {
- struct ifnet *ifp = rpp->rp_ifp;
- struct ifprefix *ifpr;
-
- if ((ifpr = ifp->if_prefixlist) != NULL) {
- for ( ; ifpr->ifpr_next;
- ifpr = ifpr->ifpr_next)
- continue;
- ifpr->ifpr_next = rp2ifpr(rpp);
- } else
- ifp->if_prefixlist = rp2ifpr(rpp);
- rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR;
- }
- /* link rr_prefix entry to rr_prefix list */
- s = splnet();
- LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry);
- splx(s);
- }
-
- if (!new->rp_raf_auto)
- return 0;
-
- /*
- * Add an address for each interface id, if it is not yet
- * If it existed but not pointing to the prefix yet,
- * init the prefix pointer.
- */
- for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
- rap = rap->ra_entry.le_next)
- {
- if (rap->ra_addr != NULL) {
- if (rap->ra_addr->ia6_ifpr == NULL)
- rap->ra_addr->ia6_ifpr = rp2ifpr(rpp);
- continue;
- }
- add_each_addr(so, rpp, rap);
- }
- return 0;
-}
-
-static int
-add_each_prefix(struct socket *so, struct rr_prefix *rpp)
-{
- init_prefix_ltimes(rpp);
- return(rrpr_update(so, rpp));
-}
-
-static void
-rp_remove(struct rr_prefix *rpp)
-{
- int s;
-
- s = splnet();
- /* unlink rp_entry from if_prefixlist */
- {
- struct ifnet *ifp = rpp->rp_ifp;
- struct ifprefix *ifpr;
-
- if ((ifpr = ifp->if_prefixlist) == rp2ifpr(rpp))
- ifp->if_prefixlist = ifpr->ifpr_next;
- else {
- while (ifpr->ifpr_next &&
- (ifpr->ifpr_next != rp2ifpr(rpp)))
- ifpr = ifpr->ifpr_next;
- if (ifpr->ifpr_next)
- ifpr->ifpr_next = rp2ifpr(rpp)->ifpr_next;
- else
- printf("Couldn't unlink rr_prefix from ifp\n");
- }
- }
- /* unlink rp_entry from rr_prefix list */
- LIST_REMOVE(rpp, rp_entry);
- splx(s);
- free(rpp, M_IP6RR);
-}
-
-static int
-create_ra_entry(struct rp_addr **rapp)
-{
- *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR,
- M_NOWAIT);
- if (*rapp == NULL) {
- log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS"
- "for rp_addr\n", __LINE__);
- return ENOBUFS;
- }
- bzero(*rapp, sizeof(*(*rapp)));
-
- return 0;
-}
-
-static int
-init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr,
- struct rr_prefix *rpp)
-{
- struct rp_addr *orap;
-
- /* init rp */
- bzero(rpp, sizeof(*rpp));
- rpp->rp_type = IN6_PREFIX_RR;
- rpp->rp_ifp = ifpr->ifpr_ifp;
- rpp->rp_plen = ifpr->ifpr_plen;
- rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix);
- rpp->rp_prefix.sin6_family = AF_INET6;
- bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
- (char *)&irr->irr_useprefix.sin6_addr,
- sizeof(irr->irr_useprefix.sin6_addr) << 3,
- 0, irr->irr_u_uselen);
- /* copy keeplen part if necessary as necessary len */
- if (irr->irr_u_uselen < ifpr->ifpr_plen)
- bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3,
- (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3,
- irr->irr_u_uselen,
- min(ifpr->ifpr_plen - irr->irr_u_uselen,
- irr->irr_u_keeplen));
- for (orap = (ifpr2rp(ifpr)->rp_addrhead).lh_first; orap != NULL;
- orap = orap->ra_entry.le_next)
- {
- struct rp_addr *rap;
- int error = 0;
-
- if ((error = create_ra_entry(&rap)) != 0)
- return error;
- rap->ra_ifid = orap->ra_ifid;
- rap->ra_flags.anycast = (orap->ra_addr != NULL &&
- (orap->ra_addr->ia6_flags &
- IN6_IFF_ANYCAST) != 0) ? 1 : 0;
- LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry);
- }
- rpp->rp_vltime = irr->irr_vltime;
- rpp->rp_pltime = irr->irr_pltime;
- rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink :
- ifpr2rp(ifpr)->rp_raf_onlink;
- rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto :
- ifpr2rp(ifpr)->rp_raf_auto;
- /* Is some FlagMasks for rrf necessary? */
- rpp->rp_rrf = irr->irr_rrf;
- rpp->rp_origin = irr->irr_origin;
-
- return 0;
-}
-
-static void
-free_rp_entries(struct rr_prefix *rpp)
-{
- /*
- * This func is only called with rpp on stack(not on list).
- * So no splnet() here
- */
- while (rpp->rp_addrhead.lh_first != NULL)
- {
- struct rp_addr *rap;
-
- rap = LIST_FIRST(&rpp->rp_addrhead);
- LIST_REMOVE(rap, ra_entry);
- if (rap->ra_addr)
- IFAFREE(&rap->ra_addr->ia_ifa);
- free(rap, M_RR_ADDR);
- }
-}
-
-static int
-add_useprefixes(struct socket *so, struct ifnet *ifp,
- struct in6_rrenumreq *irr)
-{
- struct ifprefix *ifpr, *nextifpr;
- struct rr_prefix rp;
- int error = 0;
-
- /* add prefixes to each of marked prefix */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr)
- {
- nextifpr = ifpr->ifpr_next;
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- if (ifpr2rp(ifpr)->rp_statef_addmark) {
- if ((error = init_newprefix(irr, ifpr, &rp)) != 0)
- break;
- error = add_each_prefix(so, &rp);
- }
- }
- /* free each rp_addr entry */
- free_rp_entries(&rp);
-
- return error;
-}
-
-static void
-unprefer_prefix(struct rr_prefix *rpp)
-{
- struct rp_addr *rap;
- long time_second = time.tv_sec;
-
- for (rap = rpp->rp_addrhead.lh_first; rap != NULL;
- rap = rap->ra_entry.le_next) {
- if (rap->ra_addr == NULL)
- continue;
- rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second;
- rap->ra_addr->ia6_lifetime.ia6t_pltime = 0;
- }
-}
-
-int
-delete_each_prefix(struct rr_prefix *rpp, u_char origin)
-{
- int error = 0;
-
- if (rpp->rp_origin > origin)
- return(EPERM);
-
- while (rpp->rp_addrhead.lh_first != NULL) {
- struct rp_addr *rap;
- int s;
-
- s = splnet();
- rap = LIST_FIRST(&rpp->rp_addrhead);
- if (rap == NULL) {
- splx(s);
- break;
- }
- LIST_REMOVE(rap, ra_entry);
- splx(s);
- if (rap->ra_addr == NULL) {
- free(rap, M_RR_ADDR);
- continue;
- }
- rap->ra_addr->ia6_ifpr = NULL;
-
- in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp);
- IFAFREE(&rap->ra_addr->ia_ifa);
- free(rap, M_RR_ADDR);
- }
- rp_remove(rpp);
-
- return error;
-}
-
-static void
-delete_prefixes(struct ifnet *ifp, u_char origin)
-{
- struct ifprefix *ifpr, *nextifpr;
-
- /* delete prefixes marked as tobe deleted */
- for (ifpr = ifp->if_prefixlist; ifpr; ifpr = nextifpr)
- {
- nextifpr = ifpr->ifpr_next;
- if (ifpr->ifpr_prefix->sa_family != AF_INET6 ||
- ifpr->ifpr_type != IN6_PREFIX_RR)
- continue;
- if (ifpr2rp(ifpr)->rp_statef_delmark)
- (void)delete_each_prefix(ifpr2rp(ifpr), origin);
- }
-}
-
-static int
-link_stray_ia6s(struct rr_prefix *rpp)
-{
- struct ifaddr *ifa;
-
- for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa;
- ifa = ifa->ifa_list.tqe_next)
- {
- struct rp_addr *rap;
- struct rr_prefix *orpp;
- int error = 0;
-
- if (ifa->ifa_addr->sa_family != AF_INET6)
- continue;
- if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa)))
- continue;
-
- orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr);
- if (orpp != NULL) {
- if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp),
- rpp->rp_plen))
- log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:"
- "addr %s/%d already linked to a prefix"
- "and it matches also %s/%d\n",
- ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen,
- ip6_sprintf(RP_IN6(rpp)),
- rpp->rp_plen);
- continue;
- }
- if ((error = assign_ra_entry(rpp,
- (sizeof(rap->ra_ifid) << 3) -
- rpp->rp_plen,
- (struct in6_ifaddr *)ifa)) != 0)
- return error;
- }
- return 0;
-}
-
-/* XXX assumes that permission is already checked by the caller */
-int
-in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data,
- struct ifnet *ifp)
-{
- struct rr_prefix *rpp, rp_tmp;
- struct rp_addr *rap;
- struct in6_prefixreq *ipr = (struct in6_prefixreq *)data;
- struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data;
- struct ifaddr *ifa;
- int error = 0;
-
- /*
- * Failsafe for errneous address config program.
- * Let's hope rrenumd don't make a mistakes.
- */
- if (ipr->ipr_origin <= PR_ORIG_RA)
- ipr->ipr_origin = PR_ORIG_STATIC;
-
- switch (cmd) {
- case SIOCSGIFPREFIX_IN6:
- delmark_global_prefixes(ifp, irr);
- /* FALL THROUGH */
- case SIOCAIFPREFIX_IN6:
- case SIOCCIFPREFIX_IN6:
- /* check if preferred lifetime > valid lifetime */
- if (irr->irr_pltime > irr->irr_vltime) {
- log(LOG_NOTICE,
- "in6_prefix_ioctl: preferred lifetime"
- "(%ld) is greater than valid lifetime(%ld)\n",
- (u_long)irr->irr_pltime, (u_long)irr->irr_vltime);
- error = EINVAL;
- break;
- }
- if (mark_matched_prefixes(cmd, ifp, irr)) {
- if (irr->irr_u_uselen != 0)
- if ((error = add_useprefixes(so, ifp, irr))
- != 0)
- goto failed;
- if (cmd != SIOCAIFPREFIX_IN6)
- delete_prefixes(ifp, irr->irr_origin);
- } else
- return (EADDRNOTAVAIL);
- failed:
- unmark_prefixes(ifp);
- break;
- case SIOCGIFPREFIX_IN6:
- rpp = search_matched_prefix(ifp, ipr);
- if (rpp == NULL || ifp != rpp->rp_ifp)
- return (EADDRNOTAVAIL);
-
- ipr->ipr_origin = rpp->rp_origin;
- ipr->ipr_plen = rpp->rp_plen;
- ipr->ipr_vltime = rpp->rp_vltime;
- ipr->ipr_pltime = rpp->rp_pltime;
- ipr->ipr_flags = rpp->rp_flags;
- ipr->ipr_prefix = rpp->rp_prefix;
-
- break;
- case SIOCSIFPREFIX_IN6:
- /* check if preferred lifetime > valid lifetime */
- if (ipr->ipr_pltime > ipr->ipr_vltime) {
- log(LOG_NOTICE,
- "in6_prefix_ioctl: preferred lifetime"
- "(%ld) is greater than valid lifetime(%ld)\n",
- (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime);
- error = EINVAL;
- break;
- }
-
- /* init rp_tmp */
- bzero((caddr_t)&rp_tmp, sizeof(rp_tmp));
- rp_tmp.rp_ifp = ifp;
- rp_tmp.rp_plen = ipr->ipr_plen;
- rp_tmp.rp_prefix = ipr->ipr_prefix;
- rp_tmp.rp_vltime = ipr->ipr_vltime;
- rp_tmp.rp_pltime = ipr->ipr_pltime;
- rp_tmp.rp_flags = ipr->ipr_flags;
- rp_tmp.rp_origin = ipr->ipr_origin;
-
- /* create rp_addr entries, usually at least for lladdr */
- if ((error = link_stray_ia6s(&rp_tmp)) != 0) {
- free_rp_entries(&rp_tmp);
- break;
- }
- for (ifa = ifp->if_addrlist.tqh_first;
- ifa;
- ifa = ifa->ifa_list.tqe_next)
- {
- if (ifa->ifa_addr == NULL)
- continue; /* just for safety */
- if (ifa->ifa_addr->sa_family != AF_INET6)
- continue;
- if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)) == 0)
- continue;
-
- if ((error = create_ra_entry(&rap)) != 0) {
- free_rp_entries(&rp_tmp);
- goto bad;
- }
- /* copy interface id part */
- bit_copy((caddr_t)&rap->ra_ifid,
- sizeof(rap->ra_ifid) << 3,
- (caddr_t)IFA_IN6(ifa),
- sizeof(*IFA_IN6(ifa)) << 3,
- rp_tmp.rp_plen,
- (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen);
- /* insert into list */
- LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry);
- }
-
- error = add_each_prefix(so, &rp_tmp);
-
- /* free each rp_addr entry */
- free_rp_entries(&rp_tmp);
-
- break;
- case SIOCDIFPREFIX_IN6:
- rpp = search_matched_prefix(ifp, ipr);
- if (rpp == NULL || ifp != rpp->rp_ifp)
- return (EADDRNOTAVAIL);
-
- error = delete_each_prefix(rpp, ipr->ipr_origin);
- break;
- }
- bad:
- return error;
-}
-
-void
-in6_rr_timer(void *ignored_arg)
-{
- int s;
- struct rr_prefix *rpp;
- long time_second = time.tv_sec;
-
- timeout_set(&in6_rr_timer_ch, in6_rr_timer, NULL);
- timeout_add(&in6_rr_timer_ch, ip6_rr_prune * hz);
-
- s = splnet();
- /* expire */
- rpp = LIST_FIRST(&rr_prefix);
- while (rpp) {
- if (rpp->rp_expire && rpp->rp_expire < time_second) {
- struct rr_prefix *next_rpp;
-
- next_rpp = LIST_NEXT(rpp, rp_entry);
- delete_each_prefix(rpp, PR_ORIG_KERNEL);
- rpp = next_rpp;
- continue;
- }
- if (rpp->rp_preferred && rpp->rp_preferred < time_second)
- unprefer_prefix(rpp);
- rpp = LIST_NEXT(rpp, rp_entry);
- }
- splx(s);
-}
diff --git a/sys/netinet6/in6_prefix.h b/sys/netinet6/in6_prefix.h
deleted file mode 100644
index acaf4e1059f..00000000000
--- a/sys/netinet6/in6_prefix.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* $OpenBSD: in6_prefix.h,v 1.5 2002/03/14 01:27:12 millert Exp $ */
-/* $KAME: in6_prefix.h,v 1.10 2001/02/08 16:30:30 itojun Exp $ */
-
-/*
- * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the project nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * 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.
- */
-
-#include <sys/timeout.h>
-
-struct rr_prefix {
- struct ifprefix rp_ifpr;
- LIST_ENTRY(rr_prefix) rp_entry;
- LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead;
- struct sockaddr_in6 rp_prefix; /* prefix */
- u_int32_t rp_vltime; /* advertised valid lifetime */
- u_int32_t rp_pltime; /* advertised preferred lifetime */
- time_t rp_expire; /* expiration time of the prefix */
- time_t rp_preferred; /* preferred time of the prefix */
- struct in6_prflags rp_flags;
- u_char rp_origin; /* from where this prefix info is obtained */
- struct rp_stateflags {
- /* if some prefix should be added to this prefix */
- u_char addmark : 1;
- u_char delmark : 1; /* if this prefix will be deleted */
- } rp_stateflags;
-};
-
-#define rp_type rp_ifpr.ifpr_type
-#define rp_ifp rp_ifpr.ifpr_ifp
-#define rp_plen rp_ifpr.ifpr_plen
-
-#define rp_raf rp_flags.prf_ra
-#define rp_raf_onlink rp_flags.prf_ra.onlink
-#define rp_raf_auto rp_flags.prf_ra.autonomous
-
-#define rp_statef_addmark rp_stateflags.addmark
-#define rp_statef_delmark rp_stateflags.delmark
-
-#define rp_rrf rp_flags.prf_rr
-#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid
-#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd
-
-struct rp_addr {
- LIST_ENTRY(rp_addr) ra_entry;
- struct in6_addr ra_ifid;
- struct in6_ifaddr *ra_addr;
- struct ra_flags {
- u_char anycast : 1;
- } ra_flags;
-};
-
-#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr))
-#define rp2ifpr(rp) ((struct ifprefix *)(rp))
-
-#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr)
-
-#define RR_INFINITE_LIFETIME 0xffffffff
-
-
-LIST_HEAD(rr_prhead, rr_prefix);
-
-extern struct rr_prhead rr_prefix;
-
-void in6_rr_timer(void *);
-extern struct timeout in6_rr_timer_ch;
-int delete_each_prefix(struct rr_prefix *rpp, u_char origin);
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 71aa8830f4c..b25ee7a5826 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_var.h,v 1.19 2002/06/08 00:06:58 itojun Exp $ */
+/* $OpenBSD: in6_var.h,v 1.20 2002/06/08 21:22:02 itojun Exp $ */
/* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */
/*
@@ -80,7 +80,7 @@
* hour rule for hosts). they should never be modified by nd6_timeout or
* anywhere else.
* userland -> kernel: accept pltime/vltime
- * kernel -> userland: throuw up everything
+ * kernel -> userland: throw up everything
* in kernel: modify preferred/expire only
*/
struct in6_addrlifetime {
@@ -111,8 +111,17 @@ struct in6_ifaddr {
/* list of multicast addresses */
int ia6_flags;
- struct in6_addrlifetime ia6_lifetime; /* NULL = infty */
- struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */
+ struct in6_addrlifetime ia6_lifetime;
+ time_t ia6_createtime; /* the creation time of this address, which is
+ * currently used for temporary addresses only.
+ */
+ time_t ia6_updatetime;
+
+ /* back pointer to the ND prefix (for autoconfigured addresses only) */
+ struct nd_prefix *ia6_ndpr;
+
+ /* multicast addresses joined from the kernel */
+ LIST_HEAD(, in6_multi_mship) ia6_memberships;
};
/*
@@ -270,7 +279,8 @@ struct in6_prflags {
struct prf_ra {
u_char onlink : 1;
u_char autonomous : 1;
- u_char reserved : 6;
+ u_char router : 1;
+ u_char reserved : 5;
} prf_ra;
u_char prf_reserved1;
u_short prf_reserved2;
@@ -352,8 +362,6 @@ struct in6_rrenumreq {
#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr)
#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr)
-#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr)
-
#ifdef _KERNEL
#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
(((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
@@ -426,6 +434,10 @@ struct in6_rrenumreq {
#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */
#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */
#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */
+#define IN6_IFF_NODAD 0x20 /* don't perform DAD on this address
+ * (used only at first SIOC* call)
+ */
+#define IN6_IFF_AUTOCONF 0x40 /* autoconfigurable address. */
/* do not input/output */
#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)
@@ -491,7 +503,7 @@ struct in6_multi {
#ifdef _KERNEL
/*
* Structure used by macros below to remember position when stepping through
- * all of eht in6_multi records.
+ * all of the in6_multi records.
*/
struct in6_multistep {
struct in6_ifaddr *i_ia;
@@ -555,19 +567,19 @@ do { \
IN6_NEXT_MULTI((step), (in6m)); \
} while (0)
-int in6_ifinit(struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *,
- int);
struct in6_multi *in6_addmulti(struct in6_addr *, struct ifnet *, int *);
void in6_delmulti(struct in6_multi *);
struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *);
int in6_leavegroup(struct in6_multi_mship *);
-void in6_ifscrub(struct ifnet *, struct in6_ifaddr *);
int in6_ifindex2scopeid(int);
-int in6_mask2len(struct in6_addr *);
+int in6_mask2len(struct in6_addr *, u_char *);
int in6_control(struct socket *, u_long, caddr_t, struct ifnet *,
struct proc *);
-void in6_purgeaddr(struct ifaddr *, struct ifnet *);
+int in6_update_ifa(struct ifnet *, struct in6_aliasreq *,
+ struct in6_ifaddr *);
+void in6_purgeaddr(struct ifaddr *);
int in6if_do_dad(struct ifnet *);
+void in6_purgeif(struct ifnet *);
void in6_savemkludge(struct in6_ifaddr *);
void in6_setmaxmtu(void);
void *in6_domifattach(struct ifnet *);
@@ -582,11 +594,9 @@ int in6_addr2scopeid(struct ifnet *, struct in6_addr *);
int in6_matchlen(struct in6_addr *, struct in6_addr *);
int in6_are_prefix_equal(struct in6_addr *, struct in6_addr *, int);
void in6_prefixlen2mask(struct in6_addr *, int);
-int in6_prefix_ioctl(struct socket *, u_long, caddr_t, struct ifnet *);
-int in6_prefix_add_ifid(int, struct in6_ifaddr *);
-void in6_prefix_remove_ifid(int, struct in6_ifaddr *);
void in6_purgeprefix(struct ifnet *);
+int in6_is_addr_deprecated(struct sockaddr_in6 *);
struct inpcb;
int in6_embedscope(struct in6_addr *, const struct sockaddr_in6 *,
struct inpcb *, struct ifnet **);
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index 05195b20eed..cc0bcdaff5e 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_forward.c,v 1.22 2002/06/07 04:13:10 itojun Exp $ */
+/* $OpenBSD: ip6_forward.c,v 1.23 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $ */
/*
@@ -519,12 +519,10 @@ senderr:
return;
switch (error) {
case 0:
-#if 1
if (type == ND_REDIRECT) {
icmp6_redirect_output(mcopy, rt);
return;
}
-#endif
goto freecopy;
case EMSGSIZE:
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index f8834b95f9e..5da3722e605 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_input.c,v 1.43 2002/06/07 15:27:58 itojun Exp $ */
+/* $OpenBSD: ip6_input.c,v 1.44 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */
/*
@@ -102,7 +102,6 @@
#include <netinet/icmp6.h>
#include <netinet6/in6_ifattach.h>
#include <netinet6/nd6.h>
-#include <netinet6/in6_prefix.h>
#include <netinet6/ip6protosw.h>
@@ -167,25 +166,10 @@ ip6_init2(dummy)
void *dummy;
{
-#if 1
- /*
- * to route local address of p2p link to loopback,
- * assign loopback address first.
- */
- in6_ifattach(lo0ifp, NULL);
-#else
- /* you MUST bring lo0 up manually, in rc script. */
-#endif
-
/* nd6_timer_init */
bzero(&nd6_timer_ch, sizeof(nd6_timer_ch));
timeout_set(&nd6_timer_ch, nd6_timer, NULL);
timeout_add(&nd6_timer_ch, hz);
-
- /* router renumbering prefix list maintenance */
- bzero(&in6_rr_timer_ch, sizeof(in6_rr_timer_ch));
- timeout_set(&in6_rr_timer_ch, in6_rr_timer, (caddr_t)0);
- timeout_add(&in6_rr_timer_ch, hz);
}
/*
@@ -311,7 +295,7 @@ ip6_input(m)
* support IPv4-less kernel compilation, we cannot support SIIT
* environment at all. So, it makes more sense for us to reject any
* malicious packets for non-SIIT environment, than try to do a
- * partical support for SIIT environment.
+ * partial support for SIIT environment.
*/
if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) ||
IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) {
@@ -461,7 +445,7 @@ ip6_input(m)
* already done through looking up the routing table.
*/
IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
- &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) &&
+ &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) &&
#endif
ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) {
struct in6_ifaddr *ia6 =
@@ -489,7 +473,7 @@ ip6_input(m)
}
/*
- * FAITH(Firewall Aided Internet Translator)
+ * FAITH (Firewall Aided Internet Translator)
*/
#if defined(NFAITH) && 0 < NFAITH
if (ip6_keepfaith) {
@@ -748,6 +732,10 @@ ip6_hopopts_input(plenp, rtalertp, mp, offp)
* This function is separate from ip6_hopopts_input() in order to
* handle a case where the sending node itself process its hop-by-hop
* options header. In such a case, the function is called from ip6_output().
+ *
+ * The function assumes that hbh header is located right after the IPv6 header
+ * (RFC2460 p7), opthead is pointer into data content in m, and opthead to
+ * opthead + hbhlen is located in continuous memory region.
*/
int
ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
@@ -762,6 +750,7 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
u_int8_t *opt = opthead;
u_int16_t rtalert_val;
u_int32_t jumboplen;
+ const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh);
for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) {
switch (*opt) {
@@ -781,10 +770,13 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
ip6stat.ip6s_toosmall++;
goto bad;
}
- if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2)
- /* XXX: should we discard the packet? */
- log(LOG_ERR, "length of router alert opt is inconsitent(%d)",
- *(opt + 1));
+ if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) {
+ /* XXX stat */
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 1 - opthead);
+ return (-1);
+ }
optlen = IP6OPT_RTALERT_LEN;
bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2);
*rtalertp = ntohs(rtalert_val);
@@ -795,11 +787,13 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
ip6stat.ip6s_toosmall++;
goto bad;
}
- if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2)
- /* XXX: should we discard the packet? */
- log(LOG_ERR, "length of jumbopayload opt "
- "is inconsistent(%d)\n",
- *(opt + 1));
+ if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) {
+ /* XXX stat */
+ icmp6_error(m, ICMP6_PARAM_PROB,
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 1 - opthead);
+ return (-1);
+ }
optlen = IP6OPT_JUMBO_LEN;
/*
@@ -810,11 +804,9 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
if (ip6->ip6_plen) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt - opthead);
- return(-1);
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt - opthead);
+ return (-1);
}
/*
@@ -836,11 +828,9 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
if (*plenp != 0) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt + 2 - opthead);
- return(-1);
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 2 - opthead);
+ return (-1);
}
#endif
@@ -850,11 +840,9 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
if (jumboplen <= IPV6_MAXPACKET) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt + 2 - opthead);
- return(-1);
+ ICMP6_PARAMPROB_HEADER,
+ erroff + opt + 2 - opthead);
+ return (-1);
}
*plenp = jumboplen;
@@ -864,21 +852,20 @@ ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp)
ip6stat.ip6s_toosmall++;
goto bad;
}
- if ((optlen = ip6_unknown_opt(opt, m,
- sizeof(struct ip6_hdr) +
- sizeof(struct ip6_hbh) +
- opt - opthead)) == -1)
- return(-1);
+ optlen = ip6_unknown_opt(opt, m,
+ erroff + opt - opthead);
+ if (optlen == -1)
+ return (-1);
optlen += 2;
break;
}
}
- return(0);
+ return (0);
bad:
m_freem(m);
- return(-1);
+ return (-1);
}
/*
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 23510d0f23d..822cc034763 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_output.c,v 1.65 2002/06/07 21:47:44 itojun Exp $ */
+/* $OpenBSD: ip6_output.c,v 1.66 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
/*
@@ -427,7 +427,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
/* Source address validation */
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) &&
- (flags & IPV6_DADOUTPUT) == 0) {
+ (flags & IPV6_UNSPECSRC) == 0) {
error = EOPNOTSUPP;
ip6stat.ip6s_badscope++;
goto bad;
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 55753fa0aa4..ab4738b5e83 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_var.h,v 1.18 2002/06/07 21:47:44 itojun Exp $ */
+/* $OpenBSD: ip6_var.h,v 1.19 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */
/*
@@ -107,7 +107,7 @@ struct ip6asfrag {
u_int16_t ip6af_mff; /* more fragment bit in frag off */
};
-#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m))
+#define IP6_REASS_MBUF(ip6af) ((ip6af)->ip6af_m)
struct ip6_moptions {
struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */
@@ -202,7 +202,7 @@ struct ip6stat {
#ifdef _KERNEL
/* flags passed to ip6_output as last parameter */
-#define IPV6_DADOUTPUT 0x01 /* DAD */
+#define IPV6_UNSPECSRC 0x01 /* allow :: as the source address */
#define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */
#define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */
@@ -215,7 +215,8 @@ extern int ip6_forward_srcrt; /* forward src-routed? */
extern int ip6_use_deprecated; /* allow deprecated addr as source */
extern int ip6_rr_prune; /* router renumbering prefix
* walk list every 5 sec. */
-extern const int ip6_v6only;
+extern const int ip6_v6only;
+
extern struct socket *ip6_mrouter; /* multicast routing daemon */
extern int ip6_sendredirects; /* send IP redirects when forwarding? */
extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */
@@ -231,6 +232,7 @@ extern int ip6_dad_count; /* DupAddrDetectionTransmits */
extern u_int32_t ip6_flow_seq;
extern int ip6_auto_flowlabel;
+extern int ip6_auto_linklocal;
struct in6pcb;
struct inpcb;
@@ -280,6 +282,9 @@ int rip6_usrreq(struct socket *,
int dest6_input(struct mbuf **, int *, int);
int none_input(struct mbuf **, int *, int);
+
+struct in6_addr *in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *,
+ struct ip6_moptions *, struct route_in6 *, struct in6_addr *, int *);
#endif /* _KERNEL */
#endif /* !_NETINET6_IP6_VAR_H_ */
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index cc53bf4e7c4..81ba71a236b 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mld6.c,v 1.13 2002/03/14 01:27:12 millert Exp $ */
+/* $OpenBSD: mld6.c,v 1.14 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 itojun Exp $ */
/*
@@ -144,15 +144,16 @@ mld6_start_listening(in6m)
* (reserved) or 1 (node-local).
*/
mld6_all_nodes_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifp->if_index); /* XXX */
+ htons(in6m->in6m_ifp->if_index); /* XXX */
if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) ||
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
in6m->in6m_timer = 0;
in6m->in6m_state = MLD6_OTHERLISTENER;
} else {
mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL);
- in6m->in6m_timer = MLD6_RANDOM_DELAY(
- MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ);
+ in6m->in6m_timer =
+ MLD6_RANDOM_DELAY(MLD6_UNSOLICITED_REPORT_INTERVAL *
+ PR_FASTHZ);
in6m->in6m_state = MLD6_IREPORTEDLAST;
mld6_timers_are_running = 1;
}
@@ -164,15 +165,15 @@ mld6_stop_listening(in6m)
struct in6_multi *in6m;
{
mld6_all_nodes_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifp->if_index); /* XXX */
+ htons(in6m->in6m_ifp->if_index); /* XXX */
mld6_all_routers_linklocal.s6_addr16[1] =
- htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
+ htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
if (in6m->in6m_state == MLD6_IREPORTEDLAST &&
(!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) &&
IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL)
mld6_sendpkt(in6m, MLD6_LISTENER_DONE,
- &mld6_all_routers_linklocal);
+ &mld6_all_routers_linklocal);
}
void
@@ -237,7 +238,7 @@ mld6_input(m, off)
break; /* print error or log stat? */
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
mldh->mld6_addr.s6_addr16[1] =
- htons(ifp->if_index); /* XXX */
+ htons(ifp->if_index); /* XXX */
/*
* - Start the timers in all of our membership records
@@ -282,14 +283,13 @@ mld6_input(m, off)
if (timer == 0) {
/* send a report immediately */
mld6_sendpkt(in6m, MLD6_LISTENER_REPORT,
- NULL);
+ NULL);
in6m->in6m_timer = 0; /* reset timer */
in6m->in6m_state = MLD6_IREPORTEDLAST;
- }
- else if (in6m->in6m_timer == 0 || /* idle */
+ } else if (in6m->in6m_timer == 0 || /* idle */
in6m->in6m_timer > timer) {
in6m->in6m_timer =
- MLD6_RANDOM_DELAY(timer);
+ MLD6_RANDOM_DELAY(timer);
mld6_timers_are_running = 1;
}
}
@@ -332,6 +332,11 @@ mld6_input(m, off)
break;
default: /* this is impossible */
#if 0
+ /*
+ * this case should be impossible because of filtering in
+ * icmp6_input(). But we explicitly disabled this part
+ * just in case.
+ */
log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type);
#endif
break;
@@ -355,7 +360,6 @@ mld6_fasttimeo()
return;
s = splnet();
-
mld6_timers_are_running = 0;
IN6_FIRST_MULTI(step, in6m);
while (in6m != NULL) {
@@ -384,15 +388,19 @@ mld6_sendpkt(in6m, type, dst)
struct ip6_moptions im6o;
struct in6_ifaddr *ia;
struct ifnet *ifp = in6m->in6m_ifp;
- struct ifnet *outif = NULL;
+ int ignflags;
/*
* At first, find a link local address on the outgoing interface
* to use as the source address of the MLD packet.
+ * We do not reject tentative addresses for MLD report to deal with
+ * the case where we first join a link-local address.
*/
- if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST))
- == NULL)
+ ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
+ if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL)
return;
+ if ((ia->ia6_flags & IN6_IFF_TENTATIVE))
+ ia = NULL;
/*
* Allocate mbufs to store ip6 header and MLD header.
@@ -409,6 +417,7 @@ mld6_sendpkt(in6m, type, dst)
}
mh->m_next = md;
+ mh->m_pkthdr.rcvif = NULL;
mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr);
mh->m_len = sizeof(struct ip6_hdr);
MH_ALIGN(mh, sizeof(struct ip6_hdr));
@@ -421,7 +430,7 @@ mld6_sendpkt(in6m, type, dst)
/* ip6_plen will be set later */
ip6->ip6_nxt = IPPROTO_ICMPV6;
/* ip6_hlim will be set by im6o.im6o_multicast_hlim */
- ip6->ip6_src = ia->ia_addr.sin6_addr;
+ ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
/* fill in the MLD header */
@@ -437,7 +446,7 @@ mld6_sendpkt(in6m, type, dst)
if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr))
mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */
mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
- sizeof(struct mld6_hdr));
+ sizeof(struct mld6_hdr));
/* construct multicast option */
bzero(&im6o, sizeof(im6o));
@@ -452,20 +461,18 @@ mld6_sendpkt(in6m, type, dst)
/* increment output statictics */
icmp6stat.icp6s_outhist[type]++;
-
- ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif);
- if (outif) {
- icmp6_ifstat_inc(outif, ifs6_out_msg);
- switch (type) {
- case MLD6_LISTENER_QUERY:
- icmp6_ifstat_inc(outif, ifs6_out_mldquery);
- break;
- case MLD6_LISTENER_REPORT:
- icmp6_ifstat_inc(outif, ifs6_out_mldreport);
- break;
- case MLD6_LISTENER_DONE:
- icmp6_ifstat_inc(outif, ifs6_out_mlddone);
- break;
- }
+ icmp6_ifstat_inc(ifp, ifs6_out_msg);
+ switch (type) {
+ case MLD6_LISTENER_QUERY:
+ icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
+ break;
+ case MLD6_LISTENER_REPORT:
+ icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
+ break;
+ case MLD6_LISTENER_DONE:
+ icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
+ break;
}
+
+ ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC, &im6o, NULL);
}
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index b4f788b40a1..04d8d52d50f 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: nd6.c,v 1.47 2002/06/07 17:04:19 itojun Exp $ */
-/* $KAME: nd6.c,v 1.151 2001/06/19 14:24:41 sumikawa Exp $ */
+/* $OpenBSD: nd6.c,v 1.48 2002/06/08 21:22:03 itojun Exp $ */
+/* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -60,7 +60,6 @@
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
-#include <netinet6/in6_prefix.h>
#include <netinet/icmp6.h>
#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
@@ -100,11 +99,15 @@ static struct sockaddr_in6 all1_sa;
static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *);
static void nd6_slowtimo(void *);
+static struct llinfo_nd6 *nd6_free(struct rtentry *, int);
struct timeout nd6_slowtimo_ch;
struct timeout nd6_timer_ch;
extern struct timeout in6_tmpaddrtimer_ch;
+static int fill_drlist(void *, size_t *, size_t);
+static int fill_prlist(void *, size_t *, size_t);
+
void
nd6_init()
{
@@ -146,7 +149,12 @@ nd6_ifattach(ifp)
nd->basereachable = REACHABLE_TIME;
nd->reachable = ND_COMPUTE_RTIME(nd->basereachable);
nd->retrans = RETRANS_TIMER;
- nd->flags = ND6_IFF_PERFORMNUD;
+ /*
+ * Note that the default value of ip6_accept_rtadv is 0, which means
+ * we won't accept RAs by default even if we set ND6_IFF_ACCEPT_RTADV
+ * here.
+ */
+ nd->flags = (ND6_IFF_PERFORMNUD | ND6_IFF_ACCEPT_RTADV);
/* XXX: we cannot call nd6_setmtu since ifp is not fully initialized */
nd6_setmtu0(ifp, nd);
@@ -194,7 +202,7 @@ nd6_setmtu0(ifp, ndi)
* Decreasing the interface MTU under IPV6 minimum MTU may cause
* undesirable situation. We thus notify the operator of the change
* explicitly. The check for omaxmtu is necessary to restrict the
- * log to the case of changing the MTU, not initializing it.
+ * log to the case of changing the MTU, not initializing it.
*/
if (omaxmtu >= IPV6_MMTU && ndi->maxmtu < IPV6_MMTU) {
log(LOG_NOTICE, "nd6_setmtu0: "
@@ -368,17 +376,17 @@ nd6_timer(ignored_arg)
struct nd_defrouter *dr;
struct nd_prefix *pr;
long time_second = time.tv_sec;
-
- s = splnet();
+ struct ifnet *ifp;
+ struct in6_ifaddr *ia6, *nia6;
+ struct in6_addrlifetime *lt6;
+ s = splnet();
timeout_set(&nd6_timer_ch, nd6_timer, NULL);
timeout_add(&nd6_timer_ch, nd6_prune * hz);
ln = llinfo_nd6.ln_next;
- /* XXX BSD/OS separates this code -- itojun */
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt;
- struct ifnet *ifp;
struct sockaddr_in6 *dst;
struct llinfo_nd6 *next = ln->ln_next;
/* XXX: used for the DELAY case only: */
@@ -416,25 +424,24 @@ nd6_timer(ignored_arg)
ln->ln_expire = time_second +
ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans);
nd6_ns_output(ifp, NULL, &dst->sin6_addr,
- ln, 0);
+ ln, 0);
} else {
struct mbuf *m = ln->ln_hold;
if (m) {
- if (rt->rt_ifp) {
- /*
- * Fake rcvif to make ICMP error
- * more helpful in diagnosing
- * for the receiver.
- * XXX: should we consider
- * older rcvif?
- */
- m->m_pkthdr.rcvif = rt->rt_ifp;
- }
+ /*
+ * Fake rcvif to make the ICMP error
+ * more helpful in diagnosing for the
+ * receiver.
+ * XXX: should we consider
+ * older rcvif?
+ */
+ m->m_pkthdr.rcvif = rt->rt_ifp;
+
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0);
ln->ln_hold = NULL;
}
- next = nd6_free(rt);
+ next = nd6_free(rt, 0);
}
break;
case ND6_LLINFO_REACHABLE:
@@ -447,7 +454,7 @@ nd6_timer(ignored_arg)
case ND6_LLINFO_STALE:
/* Garbage Collection(RFC 2461 5.3) */
if (ln->ln_expire)
- next = nd6_free(rt);
+ next = nd6_free(rt, 1);
break;
case ND6_LLINFO_DELAY:
@@ -458,8 +465,7 @@ nd6_timer(ignored_arg)
ln->ln_expire = time_second +
ND6_RETRANS_SEC(ndi->retrans);
nd6_ns_output(ifp, &dst->sin6_addr,
- &dst->sin6_addr,
- ln, 0);
+ &dst->sin6_addr, ln, 0);
} else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */
ln->ln_expire = time_second + nd6_gctimer;
@@ -471,14 +477,15 @@ nd6_timer(ignored_arg)
ln->ln_expire = time_second +
ND6_RETRANS_SEC(ND_IFINFO(ifp)->retrans);
nd6_ns_output(ifp, &dst->sin6_addr,
- &dst->sin6_addr, ln, 0);
- } else
- next = nd6_free(rt);
+ &dst->sin6_addr, ln, 0);
+ } else {
+ next = nd6_free(rt, 0);
+ }
break;
}
ln = next;
}
-
+
/* expire default router list */
dr = TAILQ_FIRST(&nd_defrouter);
while (dr) {
@@ -487,49 +494,51 @@ nd6_timer(ignored_arg)
t = TAILQ_NEXT(dr, dr_entry);
defrtrlist_del(dr);
dr = t;
- } else
+ } else {
dr = TAILQ_NEXT(dr, dr_entry);
+ }
}
- pr = nd_prefix.lh_first;
- while (pr) {
- struct in6_ifaddr *ia6;
- struct in6_addrlifetime *lt6;
- if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- ia6 = NULL;
- else
- ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
-
- if (ia6) {
- /* check address lifetime */
- lt6 = &ia6->ia6_lifetime;
- if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second)
- ia6->ia6_flags |= IN6_IFF_DEPRECATED;
- if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) {
- if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
- /* xxx ND_OPT_PI_FLAG_ONLINK processing */
- }
+ /*
+ * expire interface addresses.
+ * in the past the loop was inside prefix expiry processing.
+ * However, from a stricter speci-confrmance standpoint, we should
+ * rather separate address lifetimes and prefix lifetimes.
+ */
+ for (ia6 = in6_ifaddr; ia6; ia6 = nia6) {
+ nia6 = ia6->ia_next;
+ /* check address lifetime */
+ lt6 = &ia6->ia6_lifetime;
+ if (IFA6_IS_INVALID(ia6)) {
+ in6_purgeaddr(&ia6->ia_ifa);
}
+ if (IFA6_IS_DEPRECATED(ia6)) {
+ ia6->ia6_flags |= IN6_IFF_DEPRECATED;
+ } else {
+ /*
+ * A new RA might have made a deprecated address
+ * preferred.
+ */
+ ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
+ }
+ }
+ /* expire prefix list */
+ pr = nd_prefix.lh_first;
+ while (pr) {
/*
* check prefix lifetime.
* since pltime is just for autoconf, pltime processing for
* prefix is not necessary.
- *
- * we offset expire time by NDPR_KEEP_EXPIRE, so that we
- * can use the old prefix information to validate the
- * next prefix information to come. See prelist_update()
- * for actual validation.
*/
- if (pr->ndpr_expire
- && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) {
+ if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME &&
+ time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) {
struct nd_prefix *t;
t = pr->ndpr_next;
/*
* address expiration and prefix expiration are
- * separate. NEVER perform in6_ifdel here.
+ * separate. NEVER perform in6_purgeaddr here.
*/
prelist_remove(pr);
@@ -549,21 +558,28 @@ nd6_purge(ifp)
struct ifnet *ifp;
{
struct llinfo_nd6 *ln, *nln;
- struct nd_defrouter *dr, *ndr, drany;
+ struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr;
- /* Nuke default router list entries toward ifp */
- if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
- /*
- * The first entry of the list may be stored in
- * the routing table, so we'll delete it later.
- */
- for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) {
- ndr = TAILQ_NEXT(dr, dr_entry);
- if (dr->ifp == ifp)
- defrtrlist_del(dr);
- }
- dr = TAILQ_FIRST(&nd_defrouter);
+ /*
+ * Nuke default router list entries toward ifp.
+ * We defer removal of default router list entries that is installed
+ * in the routing table, in order to keep additional side effects as
+ * small as possible.
+ */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
+ ndr = TAILQ_NEXT(dr, dr_entry);
+ if (dr->installed)
+ continue;
+
+ if (dr->ifp == ifp)
+ defrtrlist_del(dr);
+ }
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
+ ndr = TAILQ_NEXT(dr, dr_entry);
+ if (!dr->installed)
+ continue;
+
if (dr->ifp == ifp)
defrtrlist_del(dr);
}
@@ -572,8 +588,14 @@ nd6_purge(ifp)
for (pr = nd_prefix.lh_first; pr; pr = npr) {
npr = pr->ndpr_next;
if (pr->ndpr_ifp == ifp) {
- if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+ /*
+ * Previously, pr->ndpr_addr is removed as well,
+ * but I strongly believe we don't have to do it.
+ * nd6_purge() is only called from in6_ifdetach(),
+ * which removes all the associated interface addresses
+ * by itself.
+ * (jinmei@kame.net 20010129)
+ */
prelist_remove(pr);
}
}
@@ -584,8 +606,6 @@ nd6_purge(ifp)
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
/* refresh default router list */
- bzero(&drany, sizeof(drany));
- defrouter_delreq(&drany, 0);
defrouter_select();
}
@@ -606,7 +626,7 @@ nd6_purge(ifp)
rt->rt_gateway->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)rt->rt_gateway;
if (sdl->sdl_index == ifp->if_index)
- nln = nd6_free(rt);
+ nln = nd6_free(rt, 0);
}
ln = nln;
}
@@ -625,6 +645,7 @@ nd6_lookup(addr6, create, ifp)
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr6;
+
rt = rtalloc1((struct sockaddr *)&sin6, create);
if (rt && (rt->rt_flags & RTF_LLINFO) == 0) {
/*
@@ -650,7 +671,7 @@ nd6_lookup(addr6, create, ifp)
* be covered by our own prefix.
*/
struct ifaddr *ifa =
- ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp);
+ ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp);
if (ifa == NULL)
return(NULL);
@@ -661,21 +682,22 @@ nd6_lookup(addr6, create, ifp)
* called in rtrequest via ifa->ifa_rtrequest.
*/
if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6,
- ifa->ifa_addr,
- (struct sockaddr *)&all1_sa,
- (ifa->ifa_flags |
- RTF_HOST | RTF_LLINFO) &
- ~RTF_CLONING,
- &rt)) != 0)
+ ifa->ifa_addr, (struct sockaddr *)&all1_sa,
+ (ifa->ifa_flags | RTF_HOST | RTF_LLINFO) &
+ ~RTF_CLONING, &rt)) != 0) {
+#if 0
log(LOG_ERR,
"nd6_lookup: failed to add route for a "
"neighbor(%s), errno=%d\n",
ip6_sprintf(addr6), e);
+#endif
+ return(NULL);
+ }
if (rt == NULL)
return(NULL);
if (rt->rt_llinfo) {
struct llinfo_nd6 *ln =
- (struct llinfo_nd6 *)rt->rt_llinfo;
+ (struct llinfo_nd6 *)rt->rt_llinfo;
ln->ln_state = ND6_LLINFO_NOSTATE;
}
} else
@@ -684,20 +706,25 @@ nd6_lookup(addr6, create, ifp)
rt->rt_refcnt--;
/*
* Validation for the entry.
+ * Note that the check for rt_llinfo is necessary because a cloned
+ * route from a parent route that has the L flag (e.g. the default
+ * route to a p2p interface) may have the flag, too, while the
+ * destination is not actually a neighbor.
* XXX: we can't use rt->rt_ifp to check for the interface, since
* it might be the loopback interface if the entry is for our
* own address on a non-loopback interface. Instead, we should
- * use rt->rt_ifa->ifa_ifp, which would specify the REAL interface.
+ * use rt->rt_ifa->ifa_ifp, which would specify the REAL
+ * interface.
*/
if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
- rt->rt_gateway->sa_family != AF_LINK ||
+ rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL ||
(ifp && rt->rt_ifa->ifa_ifp != ifp)) {
if (create) {
- log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n",
+ log(LOG_DEBUG,
+ "nd6_lookup: failed to lookup %s (if = %s)\n",
ip6_sprintf(addr6), ifp ? ifp->if_xname : "unspec");
- /* xxx more logs... kazu */
}
- return(0);
+ return(NULL);
}
return(rt);
}
@@ -711,38 +738,43 @@ nd6_is_addr_neighbor(addr, ifp)
struct sockaddr_in6 *addr;
struct ifnet *ifp;
{
- struct ifaddr *ifa;
- int i;
-
-#define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr)
-#define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr)
+ struct nd_prefix *pr;
+ struct rtentry *rt;
/*
* A link-local address is always a neighbor.
* XXX: we should use the sin6_scope_id field rather than the embedded
* interface index.
+ * XXX: a link does not necessarily specify a single interface.
*/
if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) &&
ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index)
return(1);
/*
- * If the address matches one of our addresses,
- * it should be a neighbor.
+ * If the address matches one of our on-link prefixes, it should be a
+ * neighbor.
*/
- for (ifa = ifp->if_addrlist.tqh_first;
- ifa;
- ifa = ifa->ifa_list.tqe_next)
- {
- if (ifa->ifa_addr->sa_family != AF_INET6)
- next: continue;
-
- for (i = 0; i < 4; i++) {
- if ((IFADDR6(ifa).s6_addr32[i] ^
- addr->sin6_addr.s6_addr32[i]) &
- IFMASK6(ifa).s6_addr32[i])
- goto next;
- }
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ if (pr->ndpr_ifp != ifp)
+ continue;
+
+ if (!(pr->ndpr_stateflags & NDPRF_ONLINK))
+ continue;
+
+ if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
+ &addr->sin6_addr, &pr->ndpr_mask))
+ return (1);
+ }
+
+ /*
+ * If the default router list is empty, all addresses are regarded
+ * as on-link, and thus, as a neighbor.
+ * XXX: we restrict the condition to hosts, because routers usually do
+ * not have the "default router list".
+ */
+ if (!ip6_forwarding && TAILQ_FIRST(&nd_defrouter) == NULL &&
+ nd6_defifindex == ifp->if_index) {
return(1);
}
@@ -750,36 +782,57 @@ nd6_is_addr_neighbor(addr, ifp)
* Even if the address matches none of our addresses, it might be
* in the neighbor cache.
*/
- if (nd6_lookup(&addr->sin6_addr, 0, ifp))
+ if ((rt = nd6_lookup(&addr->sin6_addr, 0, ifp)) != NULL)
return(1);
return(0);
-#undef IFADDR6
-#undef IFMASK6
}
/*
* Free an nd6 llinfo entry.
+ * Since the function would cause significant changes in the kernel, DO NOT
+ * make it global, unless you have a strong reason for the change, and are sure
+ * that the change is safe.
*/
-struct llinfo_nd6 *
-nd6_free(rt)
+static struct llinfo_nd6 *
+nd6_free(rt, gc)
struct rtentry *rt;
+ int gc;
{
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next;
struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
struct nd_defrouter *dr;
/*
- * Clear all destination cache entries for the neighbor.
- * XXX: is it better to restrict this to hosts?
+ * we used to have pfctlinput(PRC_HOSTDEAD) here.
+ * even though it is not harmful, it was not really necessary.
*/
- pfctlinput(PRC_HOSTDEAD, rt_key(rt));
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
int s;
s = splnet();
dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
- rt->rt_ifp);
+ rt->rt_ifp);
+
+ if (dr != NULL && dr->expire &&
+ ln->ln_state == ND6_LLINFO_STALE && gc) {
+ /*
+ * If the reason for the deletion is just garbage
+ * collection, and the neighbor is an active default
+ * router, do not delete it. Instead, reset the GC
+ * timer using the router's lifetime.
+ * Simply deleting the entry would affect default
+ * router selection, which is not necessarily a good
+ * thing, especially when we're using router preference
+ * values.
+ * XXX: the check for ln_state would be redundant,
+ * but we intentionally keep it just in case.
+ */
+ ln->ln_expire = dr->expire;
+ splx(s);
+ return(ln->ln_next);
+ }
+
if (ln->ln_router || dr) {
/*
* rt6_flush must be called whether or not the neighbor
@@ -805,20 +858,18 @@ nd6_free(rt)
*/
ln->ln_state = ND6_LLINFO_INCOMPLETE;
- if (dr == TAILQ_FIRST(&nd_defrouter)) {
- /*
- * It is used as the current default router,
- * so we have to move it to the end of the
- * list and choose a new one.
- * XXX: it is not very efficient if this is
- * the only router.
- */
- TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
- TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry);
-
- defrouter_select();
- }
+ /*
+ * Since defrouter_select() does not affect the
+ * on-link determination and MIP6 needs the check
+ * before the default router selection, we perform
+ * the check now.
+ */
pfxlist_onlink_check();
+
+ /*
+ * refresh default router list
+ */
+ defrouter_select();
}
splx(s);
}
@@ -837,7 +888,7 @@ nd6_free(rt)
* cached routes.
*/
rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0,
- rt_mask(rt), 0, (struct rtentry **)0);
+ rt_mask(rt), 0, (struct rtentry **)0);
return(next);
}
@@ -906,9 +957,42 @@ nd6_rtrequest(req, rt, info)
struct ifnet *ifp = rt->rt_ifp;
struct ifaddr *ifa;
long time_second = time.tv_sec;
+ int mine = 0;
+
+ if ((rt->rt_flags & RTF_GATEWAY) != 0)
+ return;
+
+ if (nd6_need_cache(ifp) == 0 && (rt->rt_flags & RTF_HOST) == 0) {
+ /*
+ * This is probably an interface direct route for a link
+ * which does not need neighbor caches (e.g. fe80::%lo0/64).
+ * We do not need special treatment below for such a route.
+ * Moreover, the RTF_LLINFO flag which would be set below
+ * would annoy the ndp(8) command.
+ */
+ return;
+ }
- if (rt->rt_flags & RTF_GATEWAY)
+ if (req == RTM_RESOLVE &&
+ (nd6_need_cache(ifp) == 0 || /* stf case */
+ !nd6_is_addr_neighbor((struct sockaddr_in6 *)rt_key(rt), ifp))) {
+ /*
+ * FreeBSD and BSD/OS often make a cloned host route based
+ * on a less-specific route (e.g. the default route).
+ * If the less specific route does not have a "gateway"
+ * (this is the case when the route just goes to a p2p or an
+ * stf interface), we'll mistakenly make a neighbor cache for
+ * the host route, and will see strange neighbor solicitation
+ * for the corresponding destination. In order to avoid the
+ * confusion, we check if the destination of the route is
+ * a neighbor in terms of neighbor discovery, and stop the
+ * process if not. Additionally, we remove the LLINFO flag
+ * so that ndp(8) will not try to get the neighbor information
+ * of the destination.
+ */
+ rt->rt_flags &= ~RTF_LLINFO;
return;
+ }
switch (req) {
case RTM_ADD:
@@ -937,13 +1021,13 @@ nd6_rtrequest(req, rt, info)
if (ln && ln->ln_expire == 0) {
/* kludge for desktops */
#if 0
- printf("nd6_request: time.tv_sec is zero; "
+ printf("nd6_rtrequest: time.tv_sec is zero; "
"treat it as 1\n");
#endif
ln->ln_expire = 1;
}
#endif
- if (rt->rt_flags & RTF_CLONING)
+ if ((rt->rt_flags & RTF_CLONING) != 0)
break;
}
/*
@@ -975,7 +1059,7 @@ nd6_rtrequest(req, rt, info)
#endif
/* FALLTHROUGH */
case RTM_RESOLVE:
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
+ if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) {
/*
* Address resolution isn't necessary for a point to
* point link, so we can skip this test for a p2p link.
@@ -1034,12 +1118,13 @@ nd6_rtrequest(req, rt, info)
* to the interface.
*/
ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp,
- &SIN6(rt_key(rt))->sin6_addr);
+ &SIN6(rt_key(rt))->sin6_addr);
if (ifa) {
caddr_t macp = nd6_ifptomac(ifp);
ln->ln_expire = 0;
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
+ mine = 1;
if (macp) {
Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen);
SDL(gate)->sdl_alen = ifp->if_addrlen;
@@ -1077,10 +1162,11 @@ nd6_rtrequest(req, rt, info)
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr8[12] = 0xff;
- (void)in6_addmulti(&llsol, ifp, &error);
- if (error)
- printf(
-"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error);
+ if (in6_addmulti(&llsol, ifp, &error)) {
+ nd6log((LOG_ERR, "%s: failed to join "
+ "%s (errno=%d)\n", ifp->if_xname,
+ ip6_sprintf(&llsol), error));
+ }
}
}
break;
@@ -1117,65 +1203,6 @@ nd6_rtrequest(req, rt, info)
}
}
-void
-nd6_p2p_rtrequest(req, rt, info)
- int req;
- struct rtentry *rt;
- struct rt_addrinfo *info; /* xxx unused */
-{
- struct sockaddr *gate = rt->rt_gateway;
- static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
- struct ifnet *ifp = rt->rt_ifp;
- struct ifaddr *ifa;
-
- if (rt->rt_flags & RTF_GATEWAY)
- return;
-
- switch (req) {
- case RTM_ADD:
- /*
- * There is no backward compatibility :)
- *
- * if ((rt->rt_flags & RTF_HOST) == 0 &&
- * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
- * rt->rt_flags |= RTF_CLONING;
- */
- if (rt->rt_flags & RTF_CLONING) {
- /*
- * Case 1: This route should come from
- * a route to interface.
- */
- rt_setgate(rt, rt_key(rt),
- (struct sockaddr *)&null_sdl);
- gate = rt->rt_gateway;
- SDL(gate)->sdl_type = ifp->if_type;
- SDL(gate)->sdl_index = ifp->if_index;
- break;
- }
- /* Announce a new entry if requested. */
- if (rt->rt_flags & RTF_ANNOUNCE)
- nd6_na_output(ifp,
- &SIN6(rt_key(rt))->sin6_addr,
- &SIN6(rt_key(rt))->sin6_addr,
- ip6_forwarding ? ND_NA_FLAG_ROUTER : 0,
- 1, NULL);
- /* FALLTHROUGH */
- case RTM_RESOLVE:
- /*
- * check if rt_key(rt) is one of my address assigned
- * to the interface.
- */
- ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp,
- &SIN6(rt_key(rt))->sin6_addr);
- if (ifa) {
- if (nd6_useloopback) {
- rt->rt_ifp = lo0ifp; /*XXX*/
- }
- }
- break;
- }
-}
-
int
nd6_ioctl(cmd, data, ifp)
u_long cmd;
@@ -1183,11 +1210,11 @@ nd6_ioctl(cmd, data, ifp)
struct ifnet *ifp;
{
struct in6_drlist *drl = (struct in6_drlist *)data;
- struct in6_prlist *prl = (struct in6_prlist *)data;
+ struct in6_oprlist *oprl = (struct in6_oprlist *)data;
struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
- struct nd_defrouter *dr, any;
+ struct nd_defrouter *dr;
struct nd_prefix *pr;
struct rtentry *rt;
int i = 0, error = 0;
@@ -1195,6 +1222,9 @@ nd6_ioctl(cmd, data, ifp)
switch (cmd) {
case SIOCGDRLST_IN6:
+ /*
+ * obsolete API, use sysctl under net.inet6.icmp6
+ */
bzero(drl, sizeof(*drl));
s = splnet();
dr = TAILQ_FIRST(&nd_defrouter);
@@ -1220,30 +1250,37 @@ nd6_ioctl(cmd, data, ifp)
break;
case SIOCGPRLST_IN6:
/*
+ * obsolete API, use sysctl under net.inet6.icmp6
+ *
+ * XXX the structure in6_prlist was changed in backward-
+ * incompatible manner. in6_oprlist is used for SIOCGPRLST_IN6,
+ * in6_prlist is used for nd6_sysctl() - fill_prlist().
+ */
+ /*
* XXX meaning of fields, especialy "raflags", is very
* differnet between RA prefix list and RR/static prefix list.
* how about separating ioctls into two?
*/
- bzero(prl, sizeof(*prl));
+ bzero(oprl, sizeof(*oprl));
s = splnet();
pr = nd_prefix.lh_first;
while (pr && i < PRLSTSIZ) {
struct nd_pfxrouter *pfr;
int j;
- prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr;
- prl->prefix[i].raflags = pr->ndpr_raf;
- prl->prefix[i].prefixlen = pr->ndpr_plen;
- prl->prefix[i].vltime = pr->ndpr_vltime;
- prl->prefix[i].pltime = pr->ndpr_pltime;
- prl->prefix[i].if_index = pr->ndpr_ifp->if_index;
- prl->prefix[i].expire = pr->ndpr_expire;
+ oprl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr;
+ oprl->prefix[i].raflags = pr->ndpr_raf;
+ oprl->prefix[i].prefixlen = pr->ndpr_plen;
+ oprl->prefix[i].vltime = pr->ndpr_vltime;
+ oprl->prefix[i].pltime = pr->ndpr_pltime;
+ oprl->prefix[i].if_index = pr->ndpr_ifp->if_index;
+ oprl->prefix[i].expire = pr->ndpr_expire;
pfr = pr->ndpr_advrtrs.lh_first;
j = 0;
while(pfr) {
if (j < DRLSTSIZ) {
-#define RTRADDR prl->prefix[i].advrtr[j]
+#define RTRADDR oprl->prefix[i].advrtr[j]
RTRADDR = pfr->router->rtaddr;
if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) {
/* XXX: hack for KAME */
@@ -1259,31 +1296,12 @@ nd6_ioctl(cmd, data, ifp)
j++;
pfr = pfr->pfr_next;
}
- prl->prefix[i].advrtrs = j;
- prl->prefix[i].origin = PR_ORIG_RA;
+ oprl->prefix[i].advrtrs = j;
+ oprl->prefix[i].origin = PR_ORIG_RA;
i++;
pr = pr->ndpr_next;
}
- {
- struct rr_prefix *rpp;
-
- for (rpp = LIST_FIRST(&rr_prefix); rpp;
- rpp = LIST_NEXT(rpp, rp_entry)) {
- if (i >= PRLSTSIZ)
- break;
- prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr;
- prl->prefix[i].raflags = rpp->rp_raf;
- prl->prefix[i].prefixlen = rpp->rp_plen;
- prl->prefix[i].vltime = rpp->rp_vltime;
- prl->prefix[i].pltime = rpp->rp_pltime;
- prl->prefix[i].if_index = rpp->rp_ifp->if_index;
- prl->prefix[i].expire = rpp->rp_expire;
- prl->prefix[i].advrtrs = 0;
- prl->prefix[i].origin = rpp->rp_origin;
- i++;
- }
- }
splx(s);
break;
@@ -1306,54 +1324,61 @@ nd6_ioctl(cmd, data, ifp)
ND_IFINFO(ifp)->flags = ndi->ndi.flags;
break;
case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */
- /* flush default router list */
- /*
- * xxx sumikawa: should not delete route if default
- * route equals to the top of default router list
- */
- bzero(&any, sizeof(any));
- defrouter_delreq(&any, 0);
+ /* sync kernel routing table with the default router list */
+ defrouter_reset();
defrouter_select();
- /* xxx sumikawa: flush prefix list */
break;
case SIOCSPFXFLUSH_IN6:
- {
+ {
/* flush all the prefix advertised by routers */
struct nd_prefix *pr, *next;
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
s = splnet();
-
+#endif
for (pr = nd_prefix.lh_first; pr; pr = next) {
+ struct in6_ifaddr *ia, *ia_next;
+
next = pr->ndpr_next;
- if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr);
+
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
+ continue; /* XXX */
+
+ /* do we really have to remove addresses as well? */
+ for (ia = in6_ifaddr; ia; ia = ia_next) {
+ /* ia might be removed. keep the next ptr. */
+ ia_next = ia->ia_next;
+
+ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0)
+ continue;
+
+ if (ia->ia6_ndpr == pr)
+ in6_purgeaddr(&ia->ia_ifa);
+ }
prelist_remove(pr);
}
splx(s);
break;
- }
+ }
case SIOCSRTRFLUSH_IN6:
- {
+ {
/* flush all the default routers */
struct nd_defrouter *dr, *next;
s = splnet();
- if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
- /*
- * The first entry of the list may be stored in
- * the routing table, so we'll delete it later.
- */
- for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) {
- next = TAILQ_NEXT(dr, dr_entry);
- defrtrlist_del(dr);
- }
- defrtrlist_del(TAILQ_FIRST(&nd_defrouter));
+ defrouter_reset();
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = next) {
+ next = TAILQ_NEXT(dr, dr_entry);
+ defrtrlist_del(dr);
}
+ defrouter_select();
splx(s);
break;
- }
+ }
case SIOCGNBRINFO_IN6:
- {
+ {
struct llinfo_nd6 *ln;
struct in6_addr nb_addr = nbi->addr; /* make local for safety */
@@ -1370,21 +1395,20 @@ nd6_ioctl(cmd, data, ifp)
}
s = splnet();
-
- if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) {
+ if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL ||
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) {
error = EINVAL;
splx(s);
break;
}
- ln = (struct llinfo_nd6 *)rt->rt_llinfo;
nbi->state = ln->ln_state;
nbi->asked = ln->ln_asked;
nbi->isrouter = ln->ln_router;
nbi->expire = ln->ln_expire;
splx(s);
-
+
break;
- }
+ }
case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */
ndif->ifindex = nd6_defifindex;
break;
@@ -1458,7 +1482,7 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code)
return NULL;
if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
fail:
- (void)nd6_free(rt);
+ (void)nd6_free(rt, 0);
return NULL;
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
@@ -1500,8 +1524,8 @@ fail:
}
if (!is_newentry) {
- if ((!olladdr && lladdr) /* (3) */
- || (olladdr && lladdr && llchange)) { /* (5) */
+ if ((!olladdr && lladdr) || /* (3) */
+ (olladdr && lladdr && llchange)) { /* (5) */
do_update = 1;
newstate = ND6_LLINFO_STALE;
} else /* (1-2,4) */
@@ -1535,8 +1559,7 @@ fail:
* set the 2nd argument as the 1st one.
*/
nd6_output(ifp, ifp, ln->ln_hold,
- (struct sockaddr_in6 *)rt_key(rt),
- rt);
+ (struct sockaddr_in6 *)rt_key(rt), rt);
ln->ln_hold = NULL;
}
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
@@ -1603,8 +1626,8 @@ fail:
/*
* Mark an entry with lladdr as a router.
*/
- if ((!is_newentry && (olladdr || lladdr)) /* (2-5) */
- || (is_newentry && lladdr)) { /* (7) */
+ if ((!is_newentry && (olladdr || lladdr)) || /* (2-5) */
+ (is_newentry && lladdr)) { /* (7) */
ln->ln_router = 1;
}
break;
@@ -1621,6 +1644,9 @@ fail:
* defrtrlist_update called the function as well. However, I believe
* we can compromise the overhead, since it only happens the first
* time.
+ * XXX: although defrouter_select() should not have a bad effect
+ * for those are not autoconfigured hosts, we explicitly avoid such
+ * cases for safety.
*/
if (do_update && ln->ln_router && !ip6_forwarding && ip6_accept_rtadv)
defrouter_select();
@@ -1678,36 +1704,22 @@ nd6_output(ifp, origifp, m0, dst, rt0)
if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
goto sendpkt;
- /*
- * XXX: we currently do not make neighbor cache on any interface
- * other than ARCnet, Ethernet, FDDI and GIF.
- *
- * RFC2893 says:
- * - unidirectional tunnels needs no ND
- */
- switch (ifp->if_type) {
- case IFT_ARCNET:
- case IFT_ETHER:
- case IFT_FDDI:
- case IFT_GIF: /* XXX need more cases? */
- break;
- default:
+ if (nd6_need_cache(ifp) == 0)
goto sendpkt;
- }
/*
* next hop determination. This routine is derived from ether_outpout.
*/
if (rt) {
if ((rt->rt_flags & RTF_UP) == 0) {
- if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1)) !=
- NULL)
+ if ((rt0 = rt = rtalloc1((struct sockaddr *)dst,
+ 1)) != NULL)
{
rt->rt_refcnt--;
if (rt->rt_ifp != ifp) {
/* XXX: loop care? */
return nd6_output(ifp, origifp, m0,
- dst, rt);
+ dst, rt);
}
} else
senderr(EHOSTUNREACH);
@@ -1719,7 +1731,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
/*
* We skip link-layer address resolution and NUD
* if the gateway is not a neighbor from ND point
- * of view, regardless the value of nd_ifinfo.flags.
+ * of view, regardless of the value of nd_ifinfo.flags.
* The second condition is a bit tricky; we skip
* if the gateway is our own address, which is
* sometimes used to install a route to a p2p link.
@@ -1741,7 +1753,8 @@ nd6_output(ifp, origifp, m0, dst, rt0)
goto lookup;
if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
rtfree(rt); rt = rt0;
- lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
+ lookup:
+ rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
if ((rt = rt->rt_gwroute) == 0)
senderr(EHOSTUNREACH);
}
@@ -1827,7 +1840,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
* solicitation issued in nd6_timer() may be less than the specified
* retransmission time. This should not be a problem from a practical
* point of view, because we'll typically see an immediate response
- * from the neighbor, which suppresses the succeeding solicitations.
+ * from the neighbor, which suppresses the succeeding solicitations.
*/
if (ln->ln_expire && ln->ln_asked == 0) {
ln->ln_asked++;
@@ -1836,7 +1849,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
}
return(0);
-
+
sendpkt:
#ifdef IPSEC
/*
@@ -1874,10 +1887,36 @@ nd6_output(ifp, origifp, m0, dst, rt0)
if (m)
m_freem(m);
return (error);
-}
+}
#undef senderr
int
+nd6_need_cache(ifp)
+ struct ifnet *ifp;
+{
+ /*
+ * XXX: we currently do not make neighbor cache on any interface
+ * other than ARCnet, Ethernet, FDDI and GIF.
+ *
+ * RFC2893 says:
+ * - unidirectional tunnels needs no ND
+ */
+ switch (ifp->if_type) {
+ case IFT_ARCNET:
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_IEEE1394:
+ case IFT_PROPVIRTUAL:
+ case IFT_L2VLAN:
+ case IFT_IEEE80211:
+ case IFT_GIF: /* XXX need more cases? */
+ return(1);
+ default:
+ return(0);
+ }
+}
+
+int
nd6_storelladdr(ifp, rt, m, dst, desten)
struct ifnet *ifp;
struct rtentry *rt;
@@ -1890,7 +1929,7 @@ nd6_storelladdr(ifp, rt, m, dst, desten)
if (m->m_flags & M_MCAST) {
switch (ifp->if_type) {
case IFT_ETHER:
- case IFT_FDDI:
+ case IFT_FDDI:
ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr,
desten);
return(1);
@@ -1917,7 +1956,8 @@ nd6_storelladdr(ifp, rt, m, dst, desten)
sdl = SDL(rt->rt_gateway);
if (sdl->sdl_alen == 0) {
/* this should be impossible, but we bark here for debugging */
- printf("nd6_storelladdr: sdl_alen == 0\n");
+ printf("nd6_storelladdr: sdl_alen == 0, dst=%s, if=%s\n",
+ ip6_sprintf(&SIN6(dst)->sin6_addr), ifp->if_xname);
m_freem(m);
return(0);
}
@@ -1925,3 +1965,193 @@ nd6_storelladdr(ifp, rt, m, dst, desten)
bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
return(1);
}
+
+int
+nd6_sysctl(name, oldp, oldlenp, newp, newlen)
+ int name;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ size_t ol, l;
+ int error;
+
+ error = 0;
+ l = 0;
+
+ if (newp)
+ return EPERM;
+ if (oldp && !oldlenp)
+ return EINVAL;
+ ol = oldlenp ? *oldlenp : 0;
+
+ switch (name) {
+ case ICMPV6CTL_ND6_DRLIST:
+ error = fill_drlist(oldp, oldlenp, ol);
+ break;
+
+ case ICMPV6CTL_ND6_PRLIST:
+ error = fill_prlist(oldp, oldlenp, ol);
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+
+ return(error);
+}
+
+static int
+fill_drlist(oldp, oldlenp, ol)
+ void *oldp;
+ size_t *oldlenp, ol;
+{
+ int error = 0, s;
+ struct in6_defrouter *d = NULL, *de = NULL;
+ struct nd_defrouter *dr;
+ size_t l;
+
+ s = splnet();
+
+ if (oldp) {
+ d = (struct in6_defrouter *)oldp;
+ de = (struct in6_defrouter *)((caddr_t)oldp + *oldlenp);
+ }
+ l = 0;
+
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+
+ if (oldp && d + 1 <= de) {
+ bzero(d, sizeof(*d));
+ d->rtaddr.sin6_family = AF_INET6;
+ d->rtaddr.sin6_len = sizeof(struct sockaddr_in6);
+ d->rtaddr.sin6_addr = dr->rtaddr;
+ in6_recoverscope(&d->rtaddr, &d->rtaddr.sin6_addr,
+ dr->ifp);
+ d->flags = dr->flags;
+ d->rtlifetime = dr->rtlifetime;
+ d->expire = dr->expire;
+ d->if_index = dr->ifp->if_index;
+ }
+
+ l += sizeof(*d);
+ if (d)
+ d++;
+ }
+
+ if (oldp) {
+ *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */
+ if (l > ol)
+ error = ENOMEM;
+ } else
+ *oldlenp = l;
+
+ splx(s);
+
+ return(error);
+}
+
+static int
+fill_prlist(oldp, oldlenp, ol)
+ void *oldp;
+ size_t *oldlenp, ol;
+{
+ int error = 0, s;
+ struct nd_prefix *pr;
+ struct in6_prefix *p = NULL;
+ struct in6_prefix *pe = NULL;
+ size_t l;
+
+ s = splnet();
+
+ if (oldp) {
+ p = (struct in6_prefix *)oldp;
+ pe = (struct in6_prefix *)((caddr_t)oldp + *oldlenp);
+ }
+ l = 0;
+
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ u_short advrtrs;
+ size_t advance;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in6 *s6;
+ struct nd_pfxrouter *pfr;
+
+ if (oldp && p + 1 <= pe)
+ {
+ bzero(p, sizeof(*p));
+ sin6 = (struct sockaddr_in6 *)(p + 1);
+
+ p->prefix = pr->ndpr_prefix;
+ if (in6_recoverscope(&p->prefix,
+ &p->prefix.sin6_addr, pr->ndpr_ifp) != 0)
+ log(LOG_ERR,
+ "scope error in prefix list (%s)\n",
+ ip6_sprintf(&p->prefix.sin6_addr));
+ p->raflags = pr->ndpr_raf;
+ p->prefixlen = pr->ndpr_plen;
+ p->vltime = pr->ndpr_vltime;
+ p->pltime = pr->ndpr_pltime;
+ p->if_index = pr->ndpr_ifp->if_index;
+ if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
+ p->expire = 0;
+ else {
+ time_t maxexpire;
+
+ /* XXX: we assume time_t is signed. */
+ maxexpire = (-1) &
+ ~(1 << ((sizeof(maxexpire) * 8) - 1));
+ if (pr->ndpr_vltime <
+ maxexpire - pr->ndpr_lastupdate) {
+ p->expire = pr->ndpr_lastupdate +
+ pr->ndpr_vltime;
+ } else
+ p->expire = maxexpire;
+ }
+ p->refcnt = pr->ndpr_refcnt;
+ p->flags = pr->ndpr_stateflags;
+ p->origin = PR_ORIG_RA;
+ advrtrs = 0;
+ for (pfr = pr->ndpr_advrtrs.lh_first; pfr;
+ pfr = pfr->pfr_next) {
+ if ((void *)&sin6[advrtrs + 1] > (void *)pe) {
+ advrtrs++;
+ continue;
+ }
+ s6 = &sin6[advrtrs];
+ s6->sin6_family = AF_INET6;
+ s6->sin6_len = sizeof(struct sockaddr_in6);
+ s6->sin6_addr = pfr->router->rtaddr;
+ in6_recoverscope(s6, &pfr->router->rtaddr,
+ pfr->router->ifp);
+ advrtrs++;
+ }
+ p->advrtrs = advrtrs;
+ }
+ else {
+ advrtrs = 0;
+ for (pfr = pr->ndpr_advrtrs.lh_first; pfr;
+ pfr = pfr->pfr_next)
+ advrtrs++;
+ }
+
+ advance = sizeof(*p) + sizeof(*sin6) * advrtrs;
+ l += advance;
+ if (p)
+ p = (struct in6_prefix *)((caddr_t)p + advance);
+ }
+
+ if (oldp) {
+ *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */
+ if (l > ol)
+ error = ENOMEM;
+ } else
+ *oldlenp = l;
+
+ splx(s);
+
+ return(error);
+}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index 1e46bbfc22a..efdc22adf8a 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: nd6.h,v 1.21 2002/06/07 02:33:04 itojun Exp $ */
-/* $KAME: nd6.h,v 1.93 2002/06/05 00:56:22 itojun Exp $ */
+/* $OpenBSD: nd6.h,v 1.22 2002/06/08 21:22:03 itojun Exp $ */
+/* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -93,6 +93,7 @@ struct nd_ifinfo {
};
#define ND6_IFF_PERFORMNUD 0x1
+#define ND6_IFF_ACCEPT_RTADV 0x2
#ifdef _KERNEL
#define ND_IFINFO(ifp) \
@@ -126,6 +127,32 @@ struct in6_drlist {
} defrouter[DRLSTSIZ];
};
+struct in6_defrouter {
+ struct sockaddr_in6 rtaddr;
+ u_char flags;
+ u_short rtlifetime;
+ u_long expire;
+ u_short if_index;
+};
+
+#ifdef _KERNEL
+struct in6_oprlist {
+ char ifname[IFNAMSIZ];
+ struct {
+ struct in6_addr prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_char origin;
+ u_long vltime;
+ u_long pltime;
+ u_long expire;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
+ } prefix[PRLSTSIZ];
+};
+#endif
+
struct in6_prlist {
char ifname[IFNAMSIZ];
struct {
@@ -133,15 +160,30 @@ struct in6_prlist {
struct prf_ra raflags;
u_char prefixlen;
u_char origin;
- u_long vltime;
- u_long pltime;
- u_long expire;
+ u_int32_t vltime;
+ u_int32_t pltime;
+ time_t expire;
u_short if_index;
u_short advrtrs; /* number of advertisement routers */
struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */
} prefix[PRLSTSIZ];
};
+struct in6_prefix {
+ struct sockaddr_in6 prefix;
+ struct prf_ra raflags;
+ u_char prefixlen;
+ u_char origin;
+ u_int32_t vltime;
+ u_int32_t pltime;
+ time_t expire;
+ u_int32_t flags;
+ int refcnt;
+ u_short if_index;
+ u_short advrtrs; /* number of advertisement routers */
+ /* struct sockaddr_in6 advrtr[] */
+};
+
#ifdef _KERNEL
struct in6_ondireq {
char ifname[IFNAMSIZ];
@@ -169,6 +211,10 @@ struct in6_ndifreq {
u_long ifindex;
};
+/* Prefix status */
+#define NDPRF_ONLINK 0x1
+#define NDPRF_DETACHED 0x2
+#define NDPRF_HOME 0x4
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/
@@ -192,10 +238,11 @@ TAILQ_HEAD(nd_drhead, nd_defrouter);
struct nd_defrouter {
TAILQ_ENTRY(nd_defrouter) dr_entry;
struct in6_addr rtaddr;
- u_char flags;
+ u_char flags; /* flags on RA message */
u_short rtlifetime;
u_long expire;
struct ifnet *ifp;
+ int installed; /* is installed into kernel routing table */
};
struct nd_prefix {
@@ -203,19 +250,20 @@ struct nd_prefix {
LIST_ENTRY(nd_prefix) ndpr_entry;
struct sockaddr_in6 ndpr_prefix; /* prefix */
struct in6_addr ndpr_mask; /* netmask derived from the prefix */
- struct in6_addr ndpr_addr; /* address that is derived from the prefix */
+
u_int32_t ndpr_vltime; /* advertised valid lifetime */
u_int32_t ndpr_pltime; /* advertised preferred lifetime */
+
time_t ndpr_expire; /* expiration time of the prefix */
time_t ndpr_preferred; /* preferred time of the prefix */
+ time_t ndpr_lastupdate; /* reception time of last advertisement */
+
struct prf_ra ndpr_flags;
+ u_int32_t ndpr_stateflags; /* actual state flags */
/* list of routers that advertise the prefix: */
LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs;
u_char ndpr_plen;
- struct ndpr_stateflags {
- /* if this prefix can be regarded as on-link */
- u_char onlink : 1;
- } ndpr_stateflags;
+ int ndpr_refcnt; /* reference couter from addresses */
};
#define ndpr_next ndpr_entry.le_next
@@ -223,15 +271,7 @@ struct nd_prefix {
#define ndpr_raf ndpr_flags
#define ndpr_raf_onlink ndpr_flags.onlink
#define ndpr_raf_auto ndpr_flags.autonomous
-
-#define ndpr_statef_onlink ndpr_stateflags.onlink
-#define ndpr_statef_addmark ndpr_stateflags.addmark
-
-/*
- * We keep expired prefix for certain amount of time, for validation purposes.
- * 1800s = MaxRtrAdvInterval
- */
-#define NDPR_KEEP_EXPIRED (1800 * 2)
+#define ndpr_raf_router ndpr_flags.router
/*
* Message format for use in obtaining information about prefixes
@@ -259,9 +299,6 @@ struct inet6_ndpr_msghdr {
#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid
#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd
-#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr))
-#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr))
-
struct nd_pfxrouter {
LIST_ENTRY(nd_pfxrouter) pfr_entry;
#define pfr_next pfr_entry.le_next
@@ -288,7 +325,6 @@ extern int nd6_debug;
extern struct timeout nd6_timer_ch;
/* nd6_rtr.c */
-extern struct ifnet *nd6_defifp; /* XXXYYY */
extern int nd6_defifindex;
union nd_opts {
@@ -329,10 +365,9 @@ struct rtentry *nd6_lookup(struct in6_addr *, int, struct ifnet *);
void nd6_setmtu(struct ifnet *);
void nd6_timer(void *);
void nd6_purge(struct ifnet *);
-struct llinfo_nd6 *nd6_free(struct rtentry *);
void nd6_nud_hint(struct rtentry *, struct in6_addr *, int);
int nd6_resolve(struct ifnet *, struct rtentry *,
- struct mbuf *, struct sockaddr *, u_char *);
+ struct mbuf *, struct sockaddr *, u_char *);
void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
void nd6_p2p_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
int nd6_ioctl(u_long, caddr_t, struct ifnet *);
@@ -340,17 +375,19 @@ struct rtentry *nd6_cache_lladdr(struct ifnet *, struct in6_addr *,
char *, int, int, int);
/* for test */
int nd6_output(struct ifnet *, struct ifnet *, struct mbuf *,
- struct sockaddr_in6 *, struct rtentry *);
+ struct sockaddr_in6 *, struct rtentry *);
int nd6_storelladdr(struct ifnet *, struct rtentry *, struct mbuf *,
- struct sockaddr *, u_char *);
+ struct sockaddr *, u_char *);
+int nd6_sysctl(int, void *, size_t *, void *, size_t);
+int nd6_need_cache(struct ifnet *);
/* nd6_nbr.c */
void nd6_na_input(struct mbuf *, int, int);
void nd6_na_output(struct ifnet *, struct in6_addr *,
- struct in6_addr *, u_long, int, struct sockaddr *);
+ struct in6_addr *, u_long, int, struct sockaddr *);
void nd6_ns_input(struct mbuf *, int, int);
void nd6_ns_output(struct ifnet *, struct in6_addr *,
- struct in6_addr *, struct llinfo_nd6 *, int);
+ struct in6_addr *, struct llinfo_nd6 *, int);
caddr_t nd6_ifptomac(struct ifnet *);
void nd6_dad_start(struct ifaddr *, int *);
void nd6_dad_stop(struct ifaddr *);
@@ -361,15 +398,18 @@ void nd6_rs_input(struct mbuf *, int, int);
void nd6_ra_input(struct mbuf *, int, int);
void prelist_del(struct nd_prefix *);
void defrouter_addreq(struct nd_defrouter *);
-void defrouter_delreq(struct nd_defrouter *, int);
+void defrouter_reset(void);
void defrouter_select(void);
void defrtrlist_del(struct nd_defrouter *);
void prelist_remove(struct nd_prefix *);
-int prelist_update(struct nd_prefix *, struct nd_defrouter *,
- struct mbuf *);
+int prelist_update(struct nd_prefix *, struct nd_defrouter *, struct mbuf *);
+int nd6_prelist_add(struct nd_prefix *, struct nd_defrouter *,
+ struct nd_prefix **);
+int nd6_prefix_onlink(struct nd_prefix *);
+int nd6_prefix_offlink(struct nd_prefix *);
void pfxlist_onlink_check(void);
-struct nd_defrouter *defrouter_lookup(struct in6_addr *,
- struct ifnet *);
+struct nd_defrouter *defrouter_lookup(struct in6_addr *, struct ifnet *);
+struct nd_prefix *nd6_prefix_lookup(struct nd_prefix *);
int in6_ifdel(struct ifnet *, struct in6_addr *);
int in6_init_prefix_ltimes(struct nd_prefix *ndpr);
void rt6_flush(struct in6_addr *, struct ifnet *);
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index e3819b9d051..9c1fe1f1408 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nd6_nbr.c,v 1.23 2002/05/29 07:54:59 itojun Exp $ */
+/* $OpenBSD: nd6_nbr.c,v 1.24 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */
/*
@@ -95,6 +95,7 @@ nd6_ns_input(m, off, icmp6len)
struct ifaddr *ifa;
int lladdrlen = 0;
int anycast = 0, proxy = 0, tentative = 0;
+ int router = ip6_forwarding;
int tlladdr;
union nd_opts ndopts;
struct sockaddr_dl *proxydl = NULL;
@@ -122,15 +123,15 @@ nd6_ns_input(m, off, icmp6len)
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
/* dst has to be solicited node multicast address. */
- if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL
- /* don't check ifindex portion */
- && daddr6.s6_addr32[1] == 0
- && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE
- && daddr6.s6_addr8[12] == 0xff) {
+ /* don't check ifindex portion */
+ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL &&
+ daddr6.s6_addr32[1] == 0 &&
+ daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE &&
+ daddr6.s6_addr8[12] == 0xff) {
; /*good*/
} else {
nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet "
- "(wrong ip6 dst)\n"));
+ "(wrong ip6 dst)\n"));
goto bad;
}
}
@@ -153,7 +154,7 @@ nd6_ns_input(m, off, icmp6len)
}
if (ndopts.nd_opts_src_lladdr) {
- lladdr = (char *)(ndopts.nd_opts_src_lladdr +1);
+ lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1);
lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
}
@@ -214,6 +215,7 @@ nd6_ns_input(m, off, icmp6len)
if (ifa) {
proxy = 1;
proxydl = SDL(rt->rt_gateway);
+ router = 0; /* XXX */
}
}
if (rt)
@@ -234,16 +236,14 @@ nd6_ns_input(m, off, icmp6len)
goto freeit;
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
- nd6log((LOG_INFO,
- "nd6_ns_input: lladdrlen mismatch for %s "
+ nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s "
"(if %d, NS packet %d)\n",
- ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
+ ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) {
- log(LOG_INFO,
- "nd6_ns_input: duplicate IP6 address %s\n",
+ log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n",
ip6_sprintf(&saddr6));
goto freeit;
}
@@ -286,20 +286,18 @@ nd6_ns_input(m, off, icmp6len)
saddr6 = in6addr_linklocal_allnodes;
saddr6.s6_addr16[1] = htons(ifp->if_index);
nd6_na_output(ifp, &saddr6, &taddr6,
- ((anycast || proxy || !tlladdr)
- ? 0 : ND_NA_FLAG_OVERRIDE)
- | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0),
- tlladdr, (struct sockaddr *)proxydl);
+ ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
+ (router ? ND_NA_FLAG_ROUTER : 0),
+ tlladdr, (struct sockaddr *)proxydl);
goto freeit;
}
nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0);
nd6_na_output(ifp, &saddr6, &taddr6,
- ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE)
- | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0)
- | ND_NA_FLAG_SOLICITED,
- tlladdr, (struct sockaddr *)proxydl);
+ ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
+ (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED,
+ tlladdr, (struct sockaddr *)proxydl);
freeit:
m_freem(m);
return;
@@ -331,12 +329,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
struct mbuf *m;
struct ip6_hdr *ip6;
struct nd_neighbor_solicit *nd_ns;
- struct in6_ifaddr *ia = NULL;
+ struct sockaddr_in6 src_sa, dst_sa;
struct ip6_moptions im6o;
int icmp6len;
int maxlen;
caddr_t mac;
- struct ifnet *outif = NULL;
+ struct route_in6 ro;
+
+ bzero(&ro, sizeof(ro));
if (IN6_IS_ADDR_MULTICAST(taddr6))
return;
@@ -344,13 +344,14 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
/* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_ns);
maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
- if (max_linkhdr + maxlen >= MCLBYTES) {
#ifdef DIAGNOSTIC
+ if (max_linkhdr + maxlen >= MCLBYTES) {
printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES "
"(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
-#endif
- return;
+ panic("nd6_ns_output: insufficient MCLBYTES");
+ /* NOTREACHED */
}
+#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && max_linkhdr + maxlen >= MHLEN) {
@@ -362,6 +363,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
}
if (m == NULL)
return;
+ m->m_pkthdr.rcvif = NULL;
if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
@@ -382,31 +384,23 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
/* ip6->ip6_plen will be set later */
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
+ /* determine the source and destination addresses */
+ bzero(&src_sa, sizeof(src_sa));
+ bzero(&dst_sa, sizeof(dst_sa));
+ src_sa.sin6_family = dst_sa.sin6_family = AF_INET6;
+ src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6);
if (daddr6)
- ip6->ip6_dst = *daddr6;
+ dst_sa.sin6_addr = *daddr6;
else {
- ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
- ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
- ip6->ip6_dst.s6_addr32[1] = 0;
- ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
- ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3];
- ip6->ip6_dst.s6_addr8[12] = 0xff;
+ dst_sa.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+ dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ dst_sa.sin6_addr.s6_addr32[1] = 0;
+ dst_sa.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
+ dst_sa.sin6_addr.s6_addr32[3] = taddr6->s6_addr32[3];
+ dst_sa.sin6_addr.s6_addr8[12] = 0xff;
}
+ ip6->ip6_dst = dst_sa.sin6_addr;
if (!dad) {
-#if 0 /* KAME way, exact address scope match */
- /*
- * Select a source whose scope is the same as that of the dest.
- * Typically, the dest is link-local solicitation multicast
- * (i.e. neighbor discovery) or link-local/global unicast
- * (i.e. neighbor un-reachability detection).
- */
- ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
- if (ia == NULL) {
- m_freem(m);
- return;
- }
- ip6->ip6_src = ia->ia_addr.sin6_addr;
-#else /* spec-wise correct */
/*
* RFC2461 7.2.2:
* "If the source address of the packet prompting the
@@ -420,7 +414,7 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
* (saddr6), if:
* - saddr6 is given from the caller (by giving "ln"), and
* - saddr6 belongs to the outgoing interface.
- * Otherwise, we perform a scope-wise match.
+ * Otherwise, we perform the source address selection as usual.
*/
struct ip6_hdr *hip6; /* hold ip6 */
struct in6_addr *saddr6;
@@ -435,23 +429,34 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
} else
saddr6 = NULL;
if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6))
- bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6));
+ src_sa.sin6_addr = *saddr6;
else {
- ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
- if (ia == NULL) {
- m_freem(m);
- return;
+ struct in6_addr *src0;
+ int error;
+
+ bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
+ src0 = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL,
+ &error);
+ if (src0 == NULL) {
+ nd6log((LOG_DEBUG,
+ "nd6_ns_output: source can't be "
+ "determined: dst=%s, error=%d\n",
+ ip6_sprintf(&dst_sa.sin6_addr), error));
+ goto bad;
}
- ip6->ip6_src = ia->ia_addr.sin6_addr;
+ src_sa.sin6_addr = *src0;
}
-#endif
} else {
/*
* Source address for DAD packet must always be IPv6
* unspecified address. (0::0)
+ * We actually don't have to 0-clear the address (we did it
+ * above), but we do so here explicitly to make the intention
+ * clearer.
*/
- bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));
+ bzero(&src_sa.sin6_addr, sizeof(src_sa.sin6_addr));
}
+ ip6->ip6_src = src_sa.sin6_addr;
nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);
nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
nd_ns->nd_ns_code = 0;
@@ -490,15 +495,25 @@ nd6_ns_output(ifp, daddr6, taddr6, ln, dad)
ip6->ip6_plen = htons((u_short)icmp6len);
nd_ns->nd_ns_cksum = 0;
- nd_ns->nd_ns_cksum
- = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
+ nd_ns->nd_ns_cksum =
+ in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
- ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif);
- if (outif) {
- icmp6_ifstat_inc(outif, ifs6_out_msg);
- icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit);
- }
+ ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL);
+ icmp6_ifstat_inc(ifp, ifs6_out_msg);
+ icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;
+
+ if (ro.ro_rt) { /* we don't cache this route. */
+ RTFREE(ro.ro_rt);
+ }
+ return;
+
+ bad:
+ if (ro.ro_rt) {
+ RTFREE(ro.ro_rt);
+ }
+ m_freem(m);
+ return;
}
/*
@@ -535,6 +550,7 @@ nd6_na_input(m, off, icmp6len)
struct rtentry *rt;
struct sockaddr_dl *sdl;
union nd_opts ndopts;
+ long time_second = time.tv_sec;
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
@@ -569,12 +585,11 @@ nd6_na_input(m, off, icmp6len)
ip6_sprintf(&taddr6)));
goto bad;
}
- if (IN6_IS_ADDR_MULTICAST(&daddr6))
- if (is_solicited) {
- nd6log((LOG_ERR,
- "nd6_na_input: a solicited adv is multicasted\n"));
- goto bad;
- }
+ if (is_solicited && IN6_IS_ADDR_MULTICAST(&daddr6)) {
+ nd6log((LOG_ERR,
+ "nd6_na_input: a solicited adv is multicasted\n"));
+ goto bad;
+ }
icmp6len -= sizeof(*nd_na);
nd6_option_init(nd_na + 1, icmp6len, &ndopts);
@@ -616,15 +631,15 @@ nd6_na_input(m, off, icmp6len)
}
if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
- nd6log((LOG_INFO,
- "nd6_na_input: lladdrlen mismatch for %s "
- "(if %d, NA packet %d)\n",
- ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));
+ nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s "
+ "(if %d, NA packet %d)\n", ip6_sprintf(&taddr6),
+ ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
/*
- * If no neighbor cache entry is found, NA SHOULD silently be discarded.
+ * If no neighbor cache entry is found, NA SHOULD silently be
+ * discarded.
*/
rt = nd6_lookup(&taddr6, 0, ifp);
if ((rt == NULL) ||
@@ -649,13 +664,20 @@ nd6_na_input(m, off, icmp6len)
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (ln->ln_expire)
- ln->ln_expire = time.tv_sec +
+ ln->ln_expire = time_second +
ND_IFINFO(rt->rt_ifp)->reachable;
} else {
ln->ln_state = ND6_LLINFO_STALE;
- ln->ln_expire = time.tv_sec + nd6_gctimer;
+ ln->ln_expire = time_second + nd6_gctimer;
+ }
+ if ((ln->ln_router = is_router) != 0) {
+ /*
+ * This means a router's state has changed from
+ * non-reachable to probably reachable, and might
+ * affect the status of associated prefixes..
+ */
+ pfxlist_onlink_check();
}
- ln->ln_router = is_router;
} else {
int llchange;
@@ -700,7 +722,7 @@ nd6_na_input(m, off, icmp6len)
*/
if (ln->ln_state == ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE;
- ln->ln_expire = time.tv_sec + nd6_gctimer;
+ ln->ln_expire = time_second + nd6_gctimer;
}
goto freeit;
} else if (is_override /* (2a) */
@@ -723,13 +745,13 @@ nd6_na_input(m, off, icmp6len)
ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0;
if (ln->ln_expire) {
- ln->ln_expire = time.tv_sec +
+ ln->ln_expire = time_second +
ND_IFINFO(ifp)->reachable;
}
} else {
if (lladdr && llchange) {
ln->ln_state = ND6_LLINFO_STALE;
- ln->ln_expire = time.tv_sec + nd6_gctimer;
+ ln->ln_expire = time_second + nd6_gctimer;
}
}
}
@@ -746,8 +768,13 @@ nd6_na_input(m, off, icmp6len)
in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
+ /*
+ * Lock to protect the default router list.
+ * XXX: this might be unnecessary, since this function
+ * is only called under the network software interrupt
+ * context. However, we keep it just for safety.
+ */
s = splnet();
-
dr = defrouter_lookup(in6, rt->rt_ifp);
if (dr)
defrtrlist_del(dr);
@@ -769,7 +796,7 @@ nd6_na_input(m, off, icmp6len)
ln->ln_asked = 0;
if (ln->ln_hold) {
/*
- * we assume ifp is not a p2p here, so just set the 2nd
+ * we assume ifp is not a loopback here, so just set the 2nd
* argument as the 1st one.
*/
nd6_output(ifp, ifp, ln->ln_hold,
@@ -806,23 +833,27 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
struct mbuf *m;
struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na;
- struct in6_ifaddr *ia = NULL;
struct ip6_moptions im6o;
- int icmp6len;
- int maxlen;
+ struct sockaddr_in6 src_sa, dst_sa;
+ struct in6_addr *src0;
+ int icmp6len, maxlen, error;
caddr_t mac;
- struct ifnet *outif = NULL;
+ struct route_in6 ro;
+
+ mac = NULL;
+ bzero(&ro, sizeof(ro));
/* estimate the size of message */
maxlen = sizeof(*ip6) + sizeof(*nd_na);
maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;
- if (max_linkhdr + maxlen >= MCLBYTES) {
#ifdef DIAGNOSTIC
+ if (max_linkhdr + maxlen >= MCLBYTES) {
printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES "
"(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);
-#endif
- return;
+ panic("nd6_na_output: insufficient MCLBYTES");
+ /* NOTREACHED */
}
+#endif
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m && max_linkhdr + maxlen >= MHLEN) {
@@ -834,6 +865,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
}
if (m == NULL)
return;
+ m->m_pkthdr.rcvif = NULL;
if (IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST;
@@ -844,7 +876,7 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
icmp6len = sizeof(*nd_na);
m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
- m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/
+ m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */
/* fill neighbor advertisement packet */
ip6 = mtod(m, struct ip6_hdr *);
@@ -853,26 +885,36 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
+ bzero(&src_sa, sizeof(src_sa));
+ bzero(&dst_sa, sizeof(dst_sa));
+ src_sa.sin6_len = dst_sa.sin6_len = sizeof(struct sockaddr_in6);
+ src_sa.sin6_family = dst_sa.sin6_family = AF_INET6;
+ dst_sa.sin6_addr = *daddr6;
if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {
/* reply to DAD */
- ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
- ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);
- ip6->ip6_dst.s6_addr32[1] = 0;
- ip6->ip6_dst.s6_addr32[2] = 0;
- ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
+ dst_sa.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
+ dst_sa.sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ dst_sa.sin6_addr.s6_addr32[1] = 0;
+ dst_sa.sin6_addr.s6_addr32[2] = 0;
+ dst_sa.sin6_addr.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
+
flags &= ~ND_NA_FLAG_SOLICITED;
- } else
- ip6->ip6_dst = *daddr6;
+ }
+ ip6->ip6_dst = dst_sa.sin6_addr;
/*
* Select a source whose scope is the same as that of the dest.
*/
- ia = in6_ifawithifp(ifp, &ip6->ip6_dst);
- if (ia == NULL) {
- m_freem(m);
- return;
+ bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa));
+ src0 = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &error);
+ if (src0 == NULL) {
+ nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
+ "determined: dst=%s, error=%d\n",
+ ip6_sprintf(&dst_sa.sin6_addr), error));
+ goto bad;
}
- ip6->ip6_src = ia->ia_addr.sin6_addr;
+ src_sa.sin6_addr = *src0;
+ ip6->ip6_src = src_sa.sin6_addr;
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
@@ -887,7 +929,6 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
* target lladdr option SHOULD NOT be included.
*/
if (tlladdr) {
- mac = NULL;
/*
* sdl0 != NULL indicates proxy NA. If we do proxy, use
* lladdr in sdl0. If we are not proxying (sending NA for
@@ -923,14 +964,25 @@ nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)
nd_na->nd_na_flags_reserved = flags;
nd_na->nd_na_cksum = 0;
nd_na->nd_na_cksum =
- in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
+ in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
- ip6_output(m, NULL, NULL, 0, &im6o, &outif);
- if (outif) {
- icmp6_ifstat_inc(outif, ifs6_out_msg);
- icmp6_ifstat_inc(outif, ifs6_out_neighboradvert);
- }
+ ip6_output(m, NULL, &ro, 0, &im6o, NULL);
+
+ icmp6_ifstat_inc(ifp, ifs6_out_msg);
+ icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
+
+ if (ro.ro_rt) { /* we don't cache this route. */
+ RTFREE(ro.ro_rt);
+ }
+ return;
+
+ bad:
+ if (ro.ro_rt) {
+ RTFREE(ro.ro_rt);
+ }
+ m_freem(m);
+ return;
}
caddr_t
@@ -941,6 +993,10 @@ nd6_ifptomac(ifp)
case IFT_ARCNET:
case IFT_ETHER:
case IFT_FDDI:
+ case IFT_IEEE1394:
+ case IFT_PROPVIRTUAL:
+ case IFT_L2VLAN:
+ case IFT_IEEE80211:
return ((caddr_t)(ifp + 1));
break;
default:
@@ -1064,13 +1120,13 @@ nd6_dad_start(ifa, tick)
* (re)initialization.
*/
dp->dad_ifa = ifa;
- ifa->ifa_refcnt++; /*just for safety*/
+ ifa->ifa_refcnt++; /* just for safety */
dp->dad_count = ip6_dad_count;
dp->dad_ns_icount = dp->dad_na_icount = 0;
dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
if (tick == NULL) {
nd6_dad_ns_output(dp, ifa);
- nd6_dad_starttimer(dp,
+ nd6_dad_starttimer(dp,
ND6_RETRANS_SEC(ND_IFINFO(ifa->ifa_ifp)->retrans) * hz);
} else {
int ntick;
@@ -1117,7 +1173,7 @@ nd6_dad_timer(ifa)
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
- s = splnet(); /*XXX*/
+ s = splnet(); /* XXX */
/* Sanity check */
if (ia == NULL) {
@@ -1162,7 +1218,7 @@ nd6_dad_timer(ifa)
* We have more NS to go. Send NS packet for DAD.
*/
nd6_dad_ns_output(dp, ifa);
- nd6_dad_starttimer(dp,
+ nd6_dad_starttimer(dp,
ND6_RETRANS_SEC(ND_IFINFO(ifa->ifa_ifp)->retrans) * hz);
} else {
/*
@@ -1251,10 +1307,10 @@ nd6_dad_duplicated(ifa)
return;
}
- log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, "
- "%d NA\n", ifa->ifa_ifp->if_xname,
- ip6_sprintf(&ia->ia_addr.sin6_addr),
- dp->dad_ns_icount, dp->dad_na_icount);
+ log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
+ "NS in/out=%d/%d, NA in=%d\n",
+ ifa->ifa_ifp->if_xname, ip6_sprintf(&ia->ia_addr.sin6_addr),
+ dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
ia->ia6_flags |= IN6_IFF_DUPLICATED;
@@ -1318,12 +1374,6 @@ nd6_dad_ns_input(ifa)
duplicate = 0;
dp = nd6_dad_find(ifa);
- /*
- * If it is from myself, ignore this.
- */
- if (ifp && (ifp->if_flags & IFF_LOOPBACK))
- return;
-
/* Quickhack - completely ignore DAD NS packets */
if (dad_ignore_ns) {
nd6log((LOG_INFO,
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index db6da5fb51e..e31df3d4205 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nd6_rtr.c,v 1.16 2002/06/08 00:06:58 itojun Exp $ */
+/* $OpenBSD: nd6_rtr.c,v 1.17 2002/06/08 21:22:03 itojun Exp $ */
/* $KAME: nd6_rtr.c,v 1.97 2001/02/07 11:09:13 itojun Exp $ */
/*
@@ -58,30 +58,29 @@
#define SDL(s) ((struct sockaddr_dl *)s)
+static int rtpref(struct nd_defrouter *);
static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
-static int prelist_add(struct nd_prefix *, struct nd_defrouter *);
-static struct nd_prefix *prefix_lookup(struct nd_prefix *);
-static struct in6_ifaddr *in6_ifadd(struct ifnet *, struct in6_addr *,
- struct in6_addr *, int);
+static struct in6_ifaddr *in6_ifadd(struct nd_prefix *);
static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *,
- struct nd_defrouter *);
+ struct nd_defrouter *);
static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *);
static void pfxrtr_del(struct nd_pfxrouter *);
static struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *);
-static void nd6_detach_prefix(struct nd_prefix *);
-static void nd6_attach_prefix(struct nd_prefix *);
+static void defrouter_delreq(struct nd_defrouter *);
static void defrouter_addifreq(struct ifnet *);
+static void defrouter_delifreq(void);
+static void nd6_rtmsg(int, struct rtentry *);
-static void in6_init_address_ltimes(struct nd_prefix *ndpr,
- struct in6_addrlifetime *lt6,
- int update_vltime);
+static void in6_init_address_ltimes(struct nd_prefix *,
+ struct in6_addrlifetime *);
static int rt6_deleteroute(struct radix_node *, void *);
extern int nd6_recalc_reachtm_interval;
-struct ifnet *nd6_defifp;
+static struct ifnet *nd6_defifp;
int nd6_defifindex;
+static struct ifaddr *nd6_defif_installed = NULL;
/*
* Receive Router Solicitation Message - just for routers.
@@ -161,7 +160,7 @@ nd6_rs_input(m, off, icmp6len)
nd6log((LOG_INFO,
"nd6_rs_input: lladdrlen mismatch for %s "
"(if %d, RS packet %d)\n",
- ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2));
+ ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2));
goto bad;
}
@@ -202,8 +201,15 @@ nd6_ra_input(m, off, icmp6len)
union nd_opts ndopts;
struct nd_defrouter *dr;
+ /*
+ * We only accept RAs only when
+ * the system-wide variable allows the acceptance, and
+ * per-interface variable allows RAs on the receiving interface.
+ */
if (ip6_accept_rtadv == 0)
goto freeit;
+ if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV))
+ goto freeit;
if (ip6->ip6_hlim != 255) {
nd6log((LOG_ERR,
@@ -245,6 +251,7 @@ nd6_ra_input(m, off, icmp6len)
u_int32_t advreachable = nd_ra->nd_ra_reachable;
long time_second = time.tv_sec;
+ Bzero(&dr0, sizeof(dr0));
dr0.rtaddr = saddr6;
dr0.flags = nd_ra->nd_ra_flags_reserved;
dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime);
@@ -272,8 +279,9 @@ nd6_ra_input(m, off, icmp6len)
*/
if (ndopts.nd_opts_pi) {
struct nd_opt_hdr *pt;
- struct nd_opt_prefix_info *pi;
+ struct nd_opt_prefix_info *pi = NULL;
struct nd_prefix pr;
+ long time_second = time.tv_sec;
for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi;
pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end;
@@ -326,13 +334,13 @@ nd6_ra_input(m, off, icmp6len)
pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif;
pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved &
- ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
+ ND_OPT_PI_FLAG_ONLINK) ? 1 : 0;
pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved &
- ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
+ ND_OPT_PI_FLAG_AUTO) ? 1 : 0;
pr.ndpr_plen = pi->nd_opt_pi_prefix_len;
pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time);
- pr.ndpr_pltime =
- ntohl(pi->nd_opt_pi_preferred_time);
+ pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time);
+ pr.ndpr_lastupdate = time_second;
if (in6_init_prefix_ltimes(&pr))
continue; /* prefix lifetime init failed */
@@ -345,13 +353,15 @@ nd6_ra_input(m, off, icmp6len)
* MTU
*/
if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
- u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+ u_long mtu;
u_long maxmtu;
+ mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+
/* lower bound */
if (mtu < IPV6_MMTU) {
nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option "
- "mtu=%d sent from %s, ignoring\n",
+ "mtu=%lu sent from %s, ignoring\n",
mtu, ip6_sprintf(&ip6->ip6_src)));
goto skip;
}
@@ -367,7 +377,7 @@ nd6_ra_input(m, off, icmp6len)
in6_setmaxmtu();
} else {
nd6log((LOG_INFO, "nd6_ra_input: bogus mtu "
- "mtu=%d sent from %s; "
+ "mtu=%lu sent from %s; "
"exceeds maxmtu %lu, ignoring\n",
mtu, ip6_sprintf(&ip6->ip6_src), maxmtu));
}
@@ -417,26 +427,57 @@ nd6_ra_input(m, off, icmp6len)
/*
* default router list proccessing sub routines
*/
+
+/* tell the change to user processes watching the routing socket. */
+static void
+nd6_rtmsg(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);
+ info.rti_info[RTAX_IFP] =
+ (struct sockaddr *)TAILQ_FIRST(&rt->rt_ifp->if_addrlist);
+ info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
+
+ rt_missmsg(cmd, &info, rt->rt_flags, 0);
+}
+
void
defrouter_addreq(new)
struct nd_defrouter *new;
{
struct sockaddr_in6 def, mask, gate;
+ struct rtentry *newrt = NULL;
int s;
+ int error;
Bzero(&def, sizeof(def));
Bzero(&mask, sizeof(mask));
- Bzero(&gate, sizeof(gate));
+ Bzero(&gate, sizeof(gate)); /* for safety */
- def.sin6_len = mask.sin6_len = gate.sin6_len
- = sizeof(struct sockaddr_in6);
+ def.sin6_len = mask.sin6_len = gate.sin6_len =
+ sizeof(struct sockaddr_in6);
def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
gate.sin6_addr = new->rtaddr;
+#ifndef SCOPEDROUTING
+ gate.sin6_scope_id = 0; /* XXX */
+#endif
s = splnet();
- (void)rtrequest(RTM_ADD, (struct sockaddr *)&def,
- (struct sockaddr *)&gate, (struct sockaddr *)&mask,
- RTF_GATEWAY, NULL);
+ error = rtrequest(RTM_ADD, (struct sockaddr *)&def,
+ (struct sockaddr *)&gate, (struct sockaddr *)&mask,
+ RTF_GATEWAY, &newrt);
+ if (newrt) {
+ nd6_rtmsg(RTM_ADD, newrt); /* tell user process */
+ newrt->rt_refcnt--;
+ }
+ if (error == 0)
+ new->installed = 1;
splx(s);
return;
}
@@ -448,7 +489,13 @@ defrouter_addifreq(ifp)
{
struct sockaddr_in6 def, mask;
struct ifaddr *ifa;
+ struct rtentry *newrt = NULL;
int error, flags;
+ struct rt_addrinfo info;
+
+ /* remove one if we have already installed one */
+ if (nd6_defif_installed)
+ defrouter_delifreq();
bzero(&def, sizeof(def));
bzero(&mask, sizeof(mask));
@@ -463,21 +510,72 @@ defrouter_addifreq(ifp)
if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) {
nd6log((LOG_ERR, /* better error? */
"defrouter_addifreq: failed to find an ifaddr "
- "to install a route to interface %s\n", ifp->if_xname));
+ "to install a route to interface %s\n",
+ ifp->if_xname));
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, NULL)) != 0) {
+ /* RTF_CLONING is necessary to make sure to perform ND */
+ flags = ifa->ifa_flags | RTF_CLONING;
+ bzero(&info, sizeof(info));
+ info.rti_info[RTAX_DST] = (struct sockaddr *)&def;
+ info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)ifa->ifa_addr;
+ info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask;
+ info.rti_info[RTAX_IFA] = (struct sockaddr *)ifa->ifa_addr;
+ info.rti_flags = flags;
+ error = rtrequest1(RTM_ADD, &info, &newrt);
+ if (error != 0) {
nd6log((LOG_ERR,
"defrouter_addifreq: failed to install a route to "
"interface %s (errno = %d)\n",
ifp->if_xname, error));
+
+ if (newrt) /* maybe unnecessary, but do it for safety */
+ newrt->rt_refcnt--;
+ } else {
+ if (newrt) {
+ nd6_rtmsg(RTM_ADD, newrt);
+ newrt->rt_refcnt--;
+ }
}
+
+ nd6_defif_installed = ifa;
+ ifa->ifa_refcnt++;
+}
+
+/* Remove a default route points to interface */
+static void
+defrouter_delifreq()
+{
+ struct sockaddr_in6 def, mask;
+ struct rtentry *oldrt = NULL;
+
+ if (!nd6_defif_installed)
+ return;
+
+ 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;
+
+ rtrequest(RTM_DELETE, (struct sockaddr *)&def,
+ (struct sockaddr *)nd6_defif_installed->ifa_addr,
+ (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt);
+ if (oldrt) {
+ nd6_rtmsg(RTM_DELETE, oldrt);
+ if (oldrt->rt_refcnt <= 0) {
+ /*
+ * XXX: borrowed from the RTM_DELETE case of
+ * rtrequest().
+ */
+ oldrt->rt_refcnt++;
+ rtfree(oldrt);
+ }
+ }
+
+ IFAFREE(nd6_defif_installed);
+ nd6_defif_installed = NULL;
}
struct nd_defrouter *
@@ -489,39 +587,15 @@ defrouter_lookup(addr, ifp)
for (dr = TAILQ_FIRST(&nd_defrouter); dr;
dr = TAILQ_NEXT(dr, dr_entry)) {
- if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr))
+ if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) {
return(dr);
+ }
}
return(NULL); /* search failed */
}
void
-defrouter_delreq(dr, dofree)
- struct nd_defrouter *dr;
- int dofree;
-{
- struct sockaddr_in6 def, mask, gate;
-
- Bzero(&def, sizeof(def));
- Bzero(&mask, sizeof(mask));
- Bzero(&gate, sizeof(gate));
-
- def.sin6_len = mask.sin6_len = gate.sin6_len
- = sizeof(struct sockaddr_in6);
- def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6;
- gate.sin6_addr = dr->rtaddr;
-
- rtrequest(RTM_DELETE, (struct sockaddr *)&def,
- (struct sockaddr *)&gate,
- (struct sockaddr *)&mask,
- RTF_GATEWAY, (struct rtentry **)0);
-
- if (dofree) /* XXX: necessary? */
- free(dr, M_IP6NDP);
-}
-
-void
defrtrlist_del(dr)
struct nd_defrouter *dr;
{
@@ -532,14 +606,13 @@ defrtrlist_del(dr)
* Flush all the routing table entries that use the router
* as a next hop.
*/
- if (!ip6_forwarding && ip6_accept_rtadv) {
- /* above is a good condition? */
+ if (!ip6_forwarding && ip6_accept_rtadv) /* XXX: better condition? */
rt6_flush(&dr->rtaddr, dr->ifp);
- }
-
- if (dr == TAILQ_FIRST(&nd_defrouter))
- deldr = dr; /* The router is primary. */
+ if (dr->installed) {
+ deldr = dr;
+ defrouter_delreq(dr);
+ }
TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
/*
@@ -564,87 +637,242 @@ defrtrlist_del(dr)
}
/*
- * Default Router Selection according to Section 6.3.6 of RFC 2461:
- * 1) Routers that are reachable or probably reachable should be
- * preferred.
+ * Remove the default route for a given router.
+ * This is just a subroutine function for defrouter_select(), and should
+ * not be called from anywhere else.
+ */
+static void
+defrouter_delreq(dr)
+ struct nd_defrouter *dr;
+{
+ struct sockaddr_in6 def, mask, gw;
+ struct rtentry *oldrt = NULL;
+
+ Bzero(&def, sizeof(def));
+ Bzero(&mask, sizeof(mask));
+ Bzero(&gw, sizeof(gw)); /* for safety */
+
+ def.sin6_len = mask.sin6_len = gw.sin6_len =
+ sizeof(struct sockaddr_in6);
+ def.sin6_family = mask.sin6_family = gw.sin6_family = AF_INET6;
+ gw.sin6_addr = dr->rtaddr;
+#ifndef SCOPEDROUTING
+ gw.sin6_scope_id = 0; /* XXX */
+#endif
+
+ rtrequest(RTM_DELETE, (struct sockaddr *)&def,
+ dr ? (struct sockaddr *)&gw : NULL,
+ (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt);
+ if (oldrt) {
+ nd6_rtmsg(RTM_DELETE, oldrt);
+ if (oldrt->rt_refcnt <= 0) {
+ /*
+ * XXX: borrowed from the RTM_DELETE case of
+ * rtrequest().
+ */
+ oldrt->rt_refcnt++;
+ rtfree(oldrt);
+ }
+ }
+
+ if (dr)
+ dr->installed = 0;
+}
+
+/*
+ * remove all default routes from default router list
+ */
+void
+defrouter_reset()
+{
+ struct nd_defrouter *dr;
+
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry))
+ defrouter_delreq(dr);
+ defrouter_delifreq();
+
+ /*
+ * XXX should we also nuke any default routers in the kernel, by
+ * going through them by rtalloc1()?
+ */
+}
+
+/*
+ * Default Router Selection according to Section 6.3.6 of RFC 2461 and
+ * draft-ietf-ipngwg-router-selection:
+ * 1) Routers that are reachable or probably reachable should be preferred.
+ * If we have more than one (probably) reachable router, prefer ones
+ * with the highest router preference.
* 2) When no routers on the list are known to be reachable or
* probably reachable, routers SHOULD be selected in a round-robin
- * fashion.
+ * fashion, regardless of router preference values.
* 3) If the Default Router List is empty, assume that all
* destinations are on-link.
+ *
+ * We assume nd_defrouter is sorted by router preference value.
+ * Since the code below covers both with and without router preference cases,
+ * we do not need to classify the cases by ifdef.
+ *
+ * At this moment, we do not try to install more than one default router,
+ * even when the multipath routing is available, because we're not sure about
+ * the benefits for stub hosts comparing to the risk of making the code
+ * complicated and the possibility of introducing bugs.
*/
void
defrouter_select()
{
int s = splnet();
- struct nd_defrouter *dr, anydr;
+ struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL;
struct rtentry *rt = NULL;
struct llinfo_nd6 *ln = NULL;
/*
- * Search for a (probably) reachable router from the list.
+ * This function should be called only when acting as an autoconfigured
+ * host. Although the remaining part of this function is not effective
+ * if the node is not an autoconfigured host, we explicitly exclude
+ * such cases here for safety.
*/
- 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 (ip6_forwarding || !ip6_accept_rtadv) {
+ nd6log((LOG_WARNING,
+ "defrouter_select: called unexpectedly (forwarding=%d, "
+ "accept_rtadv=%d)\n", ip6_forwarding, ip6_accept_rtadv));
+ splx(s);
+ return;
}
- 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 {
+ /*
+ * Let's handle easy case (3) first:
+ * If default router list is empty, we should probably install
+ * an interface route and assume that all destinations are on-link.
+ */
+ if (!TAILQ_FIRST(&nd_defrouter)) {
/*
- * 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.
+ * This test is meaningless due to a test at the beginning of
+ * the function, but we intentionally keep it to make the note
+ * clear.
*/
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.
- * XXX: we enable this for host only, because
- * this may override a default route installed
- * a user process (e.g. routing daemon) in a
- * router case.
*/
defrouter_addifreq(nd6_defifp);
} else {
+ /*
+ * purge the existing route.
+ * XXX: is this really correct?
+ */
+ defrouter_delifreq();
nd6log((LOG_INFO, "defrouter_select: "
"there's no default router and no default"
" interface\n"));
}
}
+ splx(s);
+ return;
+ }
+
+ /*
+ * If we have a default route for the default interface, delete it.
+ * Note that the existence of the route is checked in the delete
+ * function.
+ */
+ defrouter_delifreq();
+
+ /*
+ * Search for a (probably) reachable router from the list.
+ * We just pick up the first reachable one (if any), assuming that
+ * the ordering rule of the list described in defrtrlist_update().
+ */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if (!selected_dr &&
+ (rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln)) {
+ selected_dr = dr;
+ }
+
+ if (dr->installed && !installed_dr)
+ installed_dr = dr;
+ else if (dr->installed && installed_dr) {
+ /* this should not happen. warn for diagnosis. */
+ log(LOG_ERR, "defrouter_select: more than one router"
+ " is installed\n");
+ }
+ }
+ /*
+ * If none of the default routers was found to be reachable,
+ * round-robin the list regardless of preference.
+ * Otherwise, if we have an installed router, check if the selected
+ * (reachable) router should really be preferred to the installed one.
+ * We only prefer the new router when the old one is not reachable
+ * or when the new one has a really higher preference value.
+ */
+ if (!selected_dr) {
+ if (!installed_dr || !TAILQ_NEXT(installed_dr, dr_entry))
+ selected_dr = TAILQ_FIRST(&nd_defrouter);
+ else
+ selected_dr = TAILQ_NEXT(installed_dr, dr_entry);
+ } else if (installed_dr &&
+ (rt = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp)) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln) &&
+ rtpref(selected_dr) <= rtpref(installed_dr)) {
+ selected_dr = installed_dr;
+ }
+
+ /*
+ * If the selected router is different than the installed one,
+ * remove the installed router and install the selected one.
+ * Note that the selected router is never NULL here.
+ */
+ if (installed_dr != selected_dr) {
+ if (installed_dr)
+ defrouter_delreq(installed_dr);
+ defrouter_addreq(selected_dr);
}
splx(s);
return;
}
+/*
+ * for default router selection
+ * regards router-preference field as a 2-bit signed integer
+ */
+static int
+rtpref(struct nd_defrouter *dr)
+{
+#ifdef RTPREF
+ switch (dr->flags & ND_RA_FLAG_RTPREF_MASK) {
+ case ND_RA_FLAG_RTPREF_HIGH:
+ return RTPREF_HIGH;
+ case ND_RA_FLAG_RTPREF_MEDIUM:
+ case ND_RA_FLAG_RTPREF_RSV:
+ return RTPREF_MEDIUM;
+ case ND_RA_FLAG_RTPREF_LOW:
+ return RTPREF_LOW;
+ default:
+ /*
+ * This case should never happen. If it did, it would mean a
+ * serious bug of kernel internal. We thus always bark here.
+ * Or, can we even panic?
+ */
+ log(LOG_ERR, "rtpref: impossible RA flag %x", dr->flags);
+ return RTPREF_INVALID;
+ }
+ /* NOTREACHED */
+#else
+ return 0;
+#endif
+}
+
static struct nd_defrouter *
defrtrlist_update(new)
struct nd_defrouter *new;
@@ -658,10 +886,34 @@ defrtrlist_update(new)
defrtrlist_del(dr);
dr = NULL;
} else {
+ int oldpref = rtpref(dr);
+
/* override */
dr->flags = new->flags; /* xxx flag check */
dr->rtlifetime = new->rtlifetime;
dr->expire = new->expire;
+
+ /*
+ * If the preference does not change, there's no need
+ * to sort the entries.
+ */
+ if (rtpref(new) == oldpref) {
+ splx(s);
+ return(dr);
+ }
+
+ /*
+ * preferred router may be changed, so relocate
+ * this router.
+ * XXX: calling TAILQ_REMOVE directly is a bad manner.
+ * However, since defrtrlist_del() has many side
+ * effects, we intentionally do so here.
+ * defrouter_select() below will handle routing
+ * changes later.
+ */
+ TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
+ n = dr;
+ goto insert;
}
splx(s);
return(dr);
@@ -681,14 +933,27 @@ defrtrlist_update(new)
bzero(n, sizeof(*n));
*n = *new;
+insert:
/*
- * 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.
+ * Insert the new router in the Default Router List;
+ * The Default Router List should be in the descending order
+ * of router-preferece. Routers with the same preference are
+ * sorted in the arriving time order.
*/
- TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
- if (TAILQ_FIRST(&nd_defrouter) == n)
- defrouter_select();
+
+ /* insert at the end of the group */
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if (rtpref(n) > rtpref(dr))
+ break;
+ }
+ if (dr)
+ TAILQ_INSERT_BEFORE(dr, n, dr_entry);
+ else
+ TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry);
+
+ defrouter_select();
+
splx(s);
return(n);
@@ -735,8 +1000,8 @@ pfxrtr_del(pfr)
free(pfr, M_IP6NDP);
}
-static struct nd_prefix *
-prefix_lookup(pr)
+struct nd_prefix *
+nd6_prefix_lookup(pr)
struct nd_prefix *pr;
{
struct nd_prefix *search;
@@ -745,9 +1010,7 @@ prefix_lookup(pr)
if (pr->ndpr_ifp == search->ndpr_ifp &&
pr->ndpr_plen == search->ndpr_plen &&
in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
- &search->ndpr_prefix.sin6_addr,
- pr->ndpr_plen)
- ) {
+ &search->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
break;
}
}
@@ -755,12 +1018,12 @@ prefix_lookup(pr)
return(search);
}
-static int
-prelist_add(pr, dr)
- struct nd_prefix *pr;
+int
+nd6_prelist_add(pr, dr, newp)
+ struct nd_prefix *pr, **newp;
struct nd_defrouter *dr;
{
- struct nd_prefix *new;
+ struct nd_prefix *new = NULL;
int i, s;
new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT);
@@ -768,24 +1031,35 @@ prelist_add(pr, dr)
return ENOMEM;
bzero(new, sizeof(*new));
*new = *pr;
+ if (newp != NULL)
+ *newp = new;
/* initilization */
- new->ndpr_statef_onlink = pr->ndpr_statef_onlink;
LIST_INIT(&new->ndpr_advrtrs);
in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen);
/* make prefix in the canonical form */
for (i = 0; i < 4; i++)
new->ndpr_prefix.sin6_addr.s6_addr32[i] &=
- new->ndpr_mask.s6_addr32[i];
-
- /* xxx ND_OPT_PI_FLAG_ONLINK processing */
+ new->ndpr_mask.s6_addr32[i];
s = splnet();
-
/* link ndpr_entry to nd_prefix list */
LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry);
splx(s);
+ /* ND_OPT_PI_FLAG_ONLINK processing */
+ if (new->ndpr_raf_onlink) {
+ int e;
+
+ if ((e = nd6_prefix_onlink(new)) != 0) {
+ nd6log((LOG_ERR, "nd6_prelist_add: failed to make "
+ "the prefix %s/%d on-link on %s (errno=%d)\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, pr->ndpr_ifp->if_xname, e));
+ /* proceed anyway. XXX: is it correct? */
+ }
+ }
+
if (dr)
pfxrtr_add(new, dr);
@@ -797,9 +1071,36 @@ prelist_remove(pr)
struct nd_prefix *pr;
{
struct nd_pfxrouter *pfr, *next;
- int s;
+ int e, s;
+ /* make sure to invalidate the prefix until it is really freed. */
+ pr->ndpr_vltime = 0;
+ pr->ndpr_pltime = 0;
+#if 0
+ /*
+ * Though these flags are now meaningless, we'd rather keep the value
+ * not to confuse users when executing "ndp -p".
+ */
+ pr->ndpr_raf_onlink = 0;
+ pr->ndpr_raf_auto = 0;
+#endif
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 &&
+ (e = nd6_prefix_offlink(pr)) != 0) {
+ nd6log((LOG_ERR, "prelist_remove: failed to make %s/%d offlink "
+ "on %s, errno=%d\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, pr->ndpr_ifp->if_xname, e));
+ /* what should we do? */
+ }
+
+ if (pr->ndpr_refcnt > 0)
+ return; /* notice here? */
+
+#ifdef __NetBSD__
+ s = splsoftnet();
+#else
s = splnet();
+#endif
/* unlink ndpr_entry from nd_prefix list */
LIST_REMOVE(pr, ndpr_entry);
@@ -817,25 +1118,21 @@ prelist_remove(pr)
pfxlist_onlink_check();
}
-/*
- * NOTE: We set address lifetime to keep
- * address lifetime <= prefix lifetime
- * invariant. This is to simplify on-link determination code.
- * If onlink determination is udated, this routine may have to be updated too.
- */
int
prelist_update(new, dr, m)
struct nd_prefix *new;
struct nd_defrouter *dr; /* may be NULL */
struct mbuf *m;
{
- struct in6_ifaddr *ia6 = NULL;
+ struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL;
+ struct ifaddr *ifa;
+ struct ifnet *ifp = new->ndpr_ifp;
struct nd_prefix *pr;
int s = splnet();
-
int error = 0;
+ int newprefix = 0;
int auth;
- struct in6_addrlifetime *lt6;
+ struct in6_addrlifetime lt6_tmp;
auth = 0;
if (m) {
@@ -849,165 +1146,230 @@ prelist_update(new, dr, m)
#endif
}
- if ((pr = prefix_lookup(new)) != NULL) {
- if (pr->ndpr_ifp != new->ndpr_ifp) {
- error = EADDRNOTAVAIL;
- goto end;
- }
- /* update prefix information */
- pr->ndpr_flags = new->ndpr_flags;
- pr->ndpr_vltime = new->ndpr_vltime;
- pr->ndpr_pltime = new->ndpr_pltime;
- pr->ndpr_preferred = new->ndpr_preferred;
- pr->ndpr_expire = new->ndpr_expire;
+ if ((pr = nd6_prefix_lookup(new)) != NULL) {
+ /*
+ * nd6_prefix_lookup() ensures that pr and new have the same
+ * prefix on a same interface.
+ */
/*
- * RFC 2462 5.5.3 (d) or (e)
- * We got a prefix which we have seen in the past.
+ * Update prefix information. Note that the on-link (L) bit
+ * and the autonomous (A) bit should NOT be changed from 1
+ * to 0.
*/
- if (!new->ndpr_raf_auto)
- goto noautoconf1;
+ if (new->ndpr_raf_onlink == 1)
+ pr->ndpr_raf_onlink = 1;
+ if (new->ndpr_raf_auto == 1)
+ pr->ndpr_raf_auto = 1;
+ if (new->ndpr_raf_onlink) {
+ pr->ndpr_vltime = new->ndpr_vltime;
+ pr->ndpr_pltime = new->ndpr_pltime;
+ pr->ndpr_preferred = new->ndpr_preferred;
+ pr->ndpr_expire = new->ndpr_expire;
+ pr->ndpr_lastupdate = new->ndpr_lastupdate;
+ }
- if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- ia6 = NULL;
- else
- ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
+ if (new->ndpr_raf_onlink &&
+ (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
+ int e;
- if (ia6 == NULL) {
- /*
- * Special case:
- * (1) We have seen the prefix advertised before, but
- * we have never performed autoconfig for this prefix.
- * This is because Autonomous bit was 0 previously, or
- * autoconfig failed due to some other reasons.
- * (2) We have seen the prefix advertised before and
- * we have performed autoconfig in the past, but
- * we seem to have no interface address right now.
- * This is because the interface address have expired.
- *
- * This prefix is fresh, with respect to autoconfig
- * process.
- *
- * Add an address based on RFC 2462 5.5.3 (d).
- */
- ia6 = in6_ifadd(pr->ndpr_ifp,
- &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr,
- new->ndpr_plen);
- if (!ia6) {
- error = EADDRNOTAVAIL;
+ if ((e = nd6_prefix_onlink(pr)) != 0) {
nd6log((LOG_ERR,
- "prelist_update: failed to add a "
- "new address\n"));
- goto noautoconf1;
+ "prelist_update: failed to make "
+ "the prefix %s/%d on-link on %s "
+ "(errno=%d)\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, pr->ndpr_ifp->if_xname, e));
+ /* proceed anyway. XXX: is it correct? */
}
+ }
- lt6 = &ia6->ia6_lifetime;
+ if (dr && pfxrtr_lookup(pr, dr) == NULL)
+ pfxrtr_add(pr, dr);
+ } else {
+ struct nd_prefix *newpr = NULL;
- /* address lifetime <= prefix lifetime */
- lt6->ia6t_vltime = new->ndpr_vltime;
- lt6->ia6t_pltime = new->ndpr_pltime;
- in6_init_address_ltimes(new, lt6, 1);
- } else {
-#define TWOHOUR (120*60)
- /*
- * We have seen the prefix before, and we have added
- * interface address in the past. We still have
- * the interface address assigned.
- *
- * update address lifetime based on RFC 2462
- * 5.5.3 (e).
- */
- int update = 0;
-
- lt6 = &ia6->ia6_lifetime;
-
-#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++;
- }
+ newprefix = 1;
- /* 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;
- update++;
- } else if (auth) {
- lt6->ia6t_vltime = new->ndpr_vltime;
- update++;
- }
+ if (new->ndpr_vltime == 0)
+ goto end;
+ if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0)
+ goto end;
- /* jim bound rule is not imposed for pref lifetime */
- lt6->ia6t_pltime = new->ndpr_pltime;
-#endif
- in6_init_address_ltimes(new, lt6, update);
+ error = nd6_prelist_add(new, dr, &newpr);
+ if (error != 0 || newpr == NULL) {
+ nd6log((LOG_NOTICE, "prelist_update: "
+ "nd6_prelist_add failed for %s/%d on %s "
+ "errno=%d, returnpr=%p\n",
+ ip6_sprintf(&new->ndpr_prefix.sin6_addr),
+ new->ndpr_plen, new->ndpr_ifp->if_xname,
+ error, newpr));
+ goto end; /* we should just give up in this case. */
+ }
+
+ /*
+ * XXX: from the ND point of view, we can ignore a prefix
+ * with the on-link bit being zero. However, we need a
+ * prefix structure for references from autoconfigured
+ * addresses. Thus, we explicitly make sure that the prefix
+ * itself expires now.
+ */
+ if (newpr->ndpr_raf_onlink == 0) {
+ newpr->ndpr_vltime = 0;
+ newpr->ndpr_pltime = 0;
+ in6_init_prefix_ltimes(newpr);
}
- noautoconf1:
+ pr = newpr;
+ }
-#if 0
- /* address lifetime expire processing, RFC 2462 5.5.4. */
- if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) {
- struct in6_ifaddr *ia6;
+ /*
+ * Address autoconfiguration based on Section 5.5.3 of RFC 2462.
+ * Note that pr must be non NULL at this point.
+ */
- ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
- if (ia6)
- ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
- }
-#endif
+ /* 5.5.3 (a). Ignore the prefix without the A bit set. */
+ if (!new->ndpr_raf_auto)
+ goto end;
- if (dr && pfxrtr_lookup(pr, dr) == NULL)
- pfxrtr_add(pr, dr);
- } else {
- int error_tmp;
+ /*
+ * 5.5.3 (b). the link-local prefix should have been ignored in
+ * nd6_ra_input.
+ */
+
+ /*
+ * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime.
+ * This should have been done in nd6_ra_input.
+ */
+
+ /*
+ * 5.5.3 (d). If the prefix advertised does not match the prefix of an
+ * address already in the list, and the Valid Lifetime is not 0,
+ * form an address. Note that even a manually configured address
+ * should reject autoconfiguration of a new address.
+ */
+ for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+ {
+ struct in6_ifaddr *ifa6;
+ int ifa_plen;
+ u_int32_t storedlifetime;
+ long time_second = time.tv_sec;
- if (new->ndpr_vltime == 0) goto end;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
- bzero(&new->ndpr_addr, sizeof(struct in6_addr));
+ ifa6 = (struct in6_ifaddr *)ifa;
/*
- * RFC 2462 5.5.3 (d)
- * We got a fresh prefix. Perform some sanity checks
- * and add an interface address by appending interface ID
- * to the advertised prefix.
+ * Spec is not clear here, but I believe we should concentrate
+ * on unicast (i.e. not anycast) addresses.
+ * XXX: other ia6_flags? detached or duplicated?
*/
- if (!new->ndpr_raf_auto)
- goto noautoconf2;
-
- ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr,
- &new->ndpr_addr, new->ndpr_plen);
- if (!ia6) {
- error = EADDRNOTAVAIL;
- nd6log((LOG_ERR, "prelist_update: "
- "failed to add a new address\n"));
- goto noautoconf2;
+ if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0)
+ continue;
+
+ ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL);
+ if (ifa_plen != new->ndpr_plen ||
+ !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr,
+ &new->ndpr_prefix.sin6_addr, ifa_plen))
+ continue;
+
+ if (ia6_match == NULL) /* remember the first one */
+ ia6_match = ifa6;
+
+ if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0)
+ continue;
+
+ /*
+ * An already autoconfigured address matched. Now that we
+ * are sure there is at least one matched address, we can
+ * proceed to 5.5.3. (e): update the lifetimes according to the
+ * "two hours" rule and the privacy extension.
+ */
+#define TWOHOUR (120*60)
+ /*
+ * RFC2462 introduces the notion of StoredLifetime to the
+ * "two hours" rule as follows:
+ * the Lifetime associated with the previously autoconfigured
+ * address.
+ * Our interpretation of this definition is "the remaining
+ * lifetime to expiration at the evaluation time". One might
+ * be wondering if this interpretation is really conform to the
+ * RFC, because the text can read that "Lifetimes" are never
+ * decreased, and our definition of the "storedlifetime" below
+ * essentially reduces the "Valid Lifetime" advertised in the
+ * previous RA. But, this is due to the wording of the text,
+ * and our interpretation is the same as an author's intention.
+ * See the discussion in the IETF ipngwg ML in August 2001,
+ * with the Subject "StoredLifetime in RFC 2462".
+ */
+ lt6_tmp = ifa6->ia6_lifetime;
+ if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME)
+ storedlifetime = ND6_INFINITE_LIFETIME;
+ else if (time_second - ifa6->ia6_updatetime >
+ lt6_tmp.ia6t_vltime) {
+ /*
+ * The case of "invalid" address. We should usually
+ * not see this case.
+ */
+ storedlifetime = 0;
+ } else
+ storedlifetime = lt6_tmp.ia6t_vltime -
+ (time_second - ifa6->ia6_updatetime);
+ if (TWOHOUR < new->ndpr_vltime ||
+ storedlifetime < new->ndpr_vltime) {
+ lt6_tmp.ia6t_vltime = new->ndpr_vltime;
+ } else if (storedlifetime <= TWOHOUR
+#if 0
+ /*
+ * This condition is logically redundant, so we just
+ * omit it.
+ * See IPng 6712, 6717, and 6721.
+ */
+ && new->ndpr_vltime <= storedlifetime
+#endif
+ ) {
+ if (auth) {
+ lt6_tmp.ia6t_vltime = new->ndpr_vltime;
+ }
+ } else {
+ /*
+ * new->ndpr_vltime <= TWOHOUR &&
+ * TWOHOUR < storedlifetime
+ */
+ lt6_tmp.ia6t_vltime = TWOHOUR;
}
- /* set onlink bit if an interface route is configured */
- new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0;
- lt6 = &ia6->ia6_lifetime;
+ /* The 2 hour rule is not imposed for preferred lifetime. */
+ lt6_tmp.ia6t_pltime = new->ndpr_pltime;
- /* address lifetime <= prefix lifetime */
- lt6->ia6t_vltime = new->ndpr_vltime;
- lt6->ia6t_pltime = new->ndpr_pltime;
- in6_init_address_ltimes(new, lt6, 1);
+ in6_init_address_ltimes(pr, &lt6_tmp);
- noautoconf2:
- error_tmp = prelist_add(new, dr);
- error = error_tmp ? error_tmp : error;
+ ifa6->ia6_lifetime = lt6_tmp;
+ ifa6->ia6_updatetime = time_second;
+ }
+ if (ia6_match == NULL && new->ndpr_vltime) {
+ /*
+ * No address matched and the valid lifetime is non-zero.
+ * Create a new address.
+ */
+ if ((ia6 = in6_ifadd(new)) != NULL) {
+ /*
+ * note that we should use pr (not new) for reference.
+ */
+ pr->ndpr_refcnt++;
+ ia6->ia6_ndpr = pr;
+
+ /*
+ * A newly added address might affect the status
+ * of other addresses, so we check and update it.
+ * XXX: what if address duplication happens?
+ */
+ pfxlist_onlink_check();
+ } else {
+ /* just set an error. do not bark here. */
+ error = EADDRNOTAVAIL; /* XXX: might be unused. */
+ }
}
end:
@@ -1031,26 +1393,25 @@ find_pfxlist_reachable_router(pr)
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)) &&
+ 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 (A router is "available" if its neighbor cache
- * entry has reachable or probably reachable).
+ * that advertised the prefix (a router is "available" if its neighbor cache
+ * entry is 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 available router, we still regards
- * all the prefixes as on-link. This is because we can't tell if all the
+ * 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 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.
*/
@@ -1058,162 +1419,387 @@ void
pfxlist_onlink_check()
{
struct nd_prefix *pr;
+ struct in6_ifaddr *ifa;
/*
* 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))
+ if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
break;
}
-
- if (pr) {
+ if (pr != NULL || TAILQ_FIRST(&nd_defrouter) != NULL) {
/*
- * 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.
+ * There is at least one prefix that has a reachable router,
+ * or at least a router which probably does not advertise
+ * any prefixes. The latter would be the case when we move
+ * to a new link where we have a router that does not provide
+ * prefixes and we configure an address by hand.
+ * Detach prefixes which have no reachable advertising
+ * router, and attach other prefixes.
*/
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);
- }
+ /* XXX: a link-local prefix should never be detached */
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
+ continue;
+
+ /*
+ * we aren't interested in prefixes without the L bit
+ * set.
+ */
+ if (pr->ndpr_raf_onlink == 0)
+ continue;
+
+ if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
+ find_pfxlist_reachable_router(pr) == NULL)
+ pr->ndpr_stateflags |= NDPRF_DETACHED;
+ if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
+ find_pfxlist_reachable_router(pr) != 0)
+ pr->ndpr_stateflags &= ~NDPRF_DETACHED;
}
+ } else {
+ /* there is no prefix that has a reachable router */
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);
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
+ continue;
+
+ if (pr->ndpr_raf_onlink == 0)
+ continue;
+
+ if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0)
+ pr->ndpr_stateflags &= ~NDPRF_DETACHED;
+ }
+ }
+
+ /*
+ * Remove each interface route associated with a (just) detached
+ * prefix, and reinstall the interface route for a (just) attached
+ * prefix. Note that all attempt of reinstallation does not
+ * necessarily success, when a same prefix is shared among multiple
+ * interfaces. Such cases will be handled in nd6_prefix_onlink,
+ * so we don't have to care about them.
+ */
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ int e;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
+ continue;
+
+ if (pr->ndpr_raf_onlink == 0)
+ continue;
+
+ if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
+ (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
+ if ((e = nd6_prefix_offlink(pr)) != 0) {
+ nd6log((LOG_ERR,
+ "pfxlist_onlink_check: failed to "
+ "make %s/%d offlink, errno=%d\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, e));
+ }
+ }
+ if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
+ (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 &&
+ pr->ndpr_raf_onlink) {
+ if ((e = nd6_prefix_onlink(pr)) != 0) {
+ nd6log((LOG_ERR,
+ "pfxlist_onlink_check: failed to "
+ "make %s/%d offlink, errno=%d\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, e));
+ }
+ }
+ }
+
+ /*
+ * Changes on the prefix status might affect address status as well.
+ * Make sure that all addresses derived from an attached prefix are
+ * attached, and that all addresses derived from a detached prefix are
+ * detached. Note, however, that a manually configured address should
+ * always be attached.
+ * The precise detection logic is same as the one for prefixes.
+ */
+ for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF))
+ continue;
+
+ if (ifa->ia6_ndpr == NULL) {
+ /*
+ * This can happen when we first configure the address
+ * (i.e. the address exists, but the prefix does not).
+ * XXX: complicated relationships...
+ */
+ continue;
+ }
+
+ if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
+ break;
+ }
+ if (ifa) {
+ for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
+ continue;
+
+ if (ifa->ia6_ndpr == NULL) /* XXX: see above. */
+ continue;
+
+ if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
+ ifa->ia6_flags &= ~IN6_IFF_DETACHED;
+ else
+ ifa->ia6_flags |= IN6_IFF_DETACHED;
}
}
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);
+ for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
+ if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
+ continue;
+
+ ifa->ia6_flags &= ~IN6_IFF_DETACHED;
+ }
}
}
-static void
-nd6_detach_prefix(pr)
+int
+nd6_prefix_onlink(pr)
struct nd_prefix *pr;
{
- struct in6_ifaddr *ia6;
- struct sockaddr_in6 sa6, mask6;
+ struct ifaddr *ifa;
+ struct ifnet *ifp = pr->ndpr_ifp;
+ struct sockaddr_in6 mask6;
+ struct nd_prefix *opr;
+ u_long rtflags;
+ int error = 0;
+ struct rtentry *rt = NULL;
+
+ /* sanity check */
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
+ nd6log((LOG_ERR,
+ "nd6_prefix_onlink: %s/%d is already on-link\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen);
+ return(EEXIST));
+ }
/*
- * Delete the interface route associated with the prefix.
+ * Add the interface route associated with the prefix. Before
+ * installing the route, check if there's the same prefix on another
+ * interface, and the prefix has already installed the interface route.
+ * Although such a configuration is expected to be rare, we explicitly
+ * allow it.
*/
- bzero(&sa6, sizeof(sa6));
- sa6.sin6_family = AF_INET6;
- sa6.sin6_len = sizeof(sa6);
- bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
- sizeof(struct in6_addr));
- bzero(&mask6, sizeof(mask6));
- mask6.sin6_family = AF_INET6;
- mask6.sin6_len = sizeof(sa6);
- bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
- {
- int e;
+ for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
+ if (opr == pr)
+ continue;
+
+ if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0)
+ continue;
+
+ if (opr->ndpr_plen == pr->ndpr_plen &&
+ in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
+ &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen))
+ return(0);
+ }
- e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
- (struct sockaddr *)&mask6, 0, NULL);
- if (e) {
- nd6log((LOG_ERR,
- "nd6_detach_prefix: failed to delete route: "
- "%s/%d (errno = %d)\n",
- ip6_sprintf(&sa6.sin6_addr),
- pr->ndpr_plen,
- e));
+ /*
+ * We prefer link-local addresses as the associated interface address.
+ */
+ /* search for a link-local addr */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
+ IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
+ if (ifa == NULL) {
+ /* XXX: freebsd does not have ifa_ifwithaf */
+ for (ifa = ifp->if_addrlist.tqh_first;
+ ifa;
+ ifa = ifa->ifa_list.tqe_next)
+ {
+ if (ifa->ifa_addr->sa_family == AF_INET6)
+ break;
}
+ /* should we care about ia6_flags? */
+ }
+ if (ifa == NULL) {
+ /*
+ * This can still happen, when, for example, we receive an RA
+ * containing a prefix with the L bit set and the A bit clear,
+ * after removing all IPv6 addresses on the receiving
+ * interface. This should, of course, be rare though.
+ */
+ nd6log((LOG_NOTICE,
+ "nd6_prefix_onlink: failed to find any ifaddr"
+ " to add route for a prefix(%s/%d) on %s\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, ifp->if_xname));
+ return(0);
}
/*
- * Mark the address derived from the prefix detached so that
- * it won't be used as a source address for a new connection.
+ * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs.
+ * ifa->ifa_rtrequest = nd6_rtrequest;
*/
- if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- ia6 = NULL;
- else
- ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
- if (ia6)
- ia6->ia6_flags |= IN6_IFF_DETACHED;
+ bzero(&mask6, sizeof(mask6));
+ mask6.sin6_len = sizeof(mask6);
+ mask6.sin6_addr = pr->ndpr_mask;
+ /* rtrequest() will probably set RTF_UP, but we're not sure. */
+ rtflags = ifa->ifa_flags | RTF_UP;
+ if (nd6_need_cache(ifp)) {
+ /* explicitly set in case ifa_flags does not set the flag. */
+ rtflags |= RTF_CLONING;
+ } else {
+ /*
+ * explicitly clear the cloning bit in case ifa_flags sets it.
+ */
+ rtflags &= ~RTF_CLONING;
+ }
+ error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
+ ifa->ifa_addr, (struct sockaddr *)&mask6, rtflags, &rt);
+ if (error == 0) {
+ if (rt != NULL) /* this should be non NULL, though */
+ nd6_rtmsg(RTM_ADD, rt);
+ pr->ndpr_stateflags |= NDPRF_ONLINK;
+ } else {
+ nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a"
+ " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx "
+ "errno = %d\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr),
+ pr->ndpr_plen, ifp->if_xname,
+ ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr),
+ ip6_sprintf(&mask6.sin6_addr), rtflags, error));
+ }
+
+ if (rt != NULL)
+ rt->rt_refcnt--;
+
+ return(error);
}
-static void
-nd6_attach_prefix(pr)
+int
+nd6_prefix_offlink(pr)
struct nd_prefix *pr;
{
- struct ifaddr *ifa;
- struct in6_ifaddr *ia6;
+ int error = 0;
+ struct ifnet *ifp = pr->ndpr_ifp;
+ struct nd_prefix *opr;
+ struct sockaddr_in6 sa6, mask6;
+ struct rtentry *rt = NULL;
- /*
- * Add the interface route associated with the prefix(if necessary)
- * Should we consider if the L bit is set in pr->ndpr_flags?
- */
- ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix,
- pr->ndpr_ifp);
- if (ifa == NULL) {
+ /* sanity check */
+ if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
nd6log((LOG_ERR,
- "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));
+ "nd6_prefix_offlink: %s/%d is already off-link\n",
+ ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen));
+ return(EEXIST);
}
- else {
- int e;
- struct sockaddr_in6 mask6;
-
- bzero(&mask6, sizeof(mask6));
- mask6.sin6_family = AF_INET6;
- mask6.sin6_len = sizeof(mask6);
- mask6.sin6_addr = pr->ndpr_mask;
- e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix,
- ifa->ifa_addr, (struct sockaddr *)&mask6,
- ifa->ifa_flags, NULL);
- if (e == 0)
- pr->ndpr_statef_onlink = 1;
- else {
- nd6log((LOG_ERR,
- "nd6_attach_prefix: failed to add route for"
- " a prefix(%s/%d), errno = %d\n",
- ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e));
+
+ bzero(&sa6, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_len = sizeof(sa6);
+ bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr,
+ sizeof(struct in6_addr));
+ bzero(&mask6, sizeof(mask6));
+ mask6.sin6_family = AF_INET6;
+ mask6.sin6_len = sizeof(sa6);
+ bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr));
+ error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL,
+ (struct sockaddr *)&mask6, 0, &rt);
+ if (error == 0) {
+ pr->ndpr_stateflags &= ~NDPRF_ONLINK;
+
+ /* report the route deletion to the routing socket. */
+ if (rt != NULL)
+ nd6_rtmsg(RTM_DELETE, rt);
+
+ /*
+ * There might be the same prefix on another interface,
+ * the prefix which could not be on-link just because we have
+ * the interface route (see comments in nd6_prefix_onlink).
+ * If there's one, try to make the prefix on-link on the
+ * interface.
+ */
+ for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
+ if (opr == pr)
+ continue;
+
+ if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0)
+ continue;
+
+ /*
+ * KAME specific: detached prefixes should not be
+ * on-link.
+ */
+ if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0)
+ continue;
+
+ if (opr->ndpr_plen == pr->ndpr_plen &&
+ in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr,
+ &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) {
+ int e;
+
+ if ((e = nd6_prefix_onlink(opr)) != 0) {
+ nd6log((LOG_ERR,
+ "nd6_prefix_offlink: failed to "
+ "recover a prefix %s/%d from %s "
+ "to %s (errno = %d)\n",
+ ip6_sprintf(&opr->ndpr_prefix.sin6_addr),
+ opr->ndpr_plen, ifp->if_xname,
+ opr->ndpr_ifp->if_xname, e));
+ }
+ }
}
+ } else {
+ /* XXX: can we still set the NDPRF_ONLINK flag? */
+ nd6log((LOG_ERR,
+ "nd6_prefix_offlink: failed to delete route: "
+ "%s/%d on %s (errno = %d)\n",
+ ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, ifp->if_xname,
+ error));
}
- /*
- * Now the address derived from the prefix can be used as a source
- * for a new connection, so clear the detached flag.
- */
- if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr))
- ia6 = NULL;
- else
- ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
- if (ia6) {
- ia6->ia6_flags &= ~IN6_IFF_DETACHED;
- if (pr->ndpr_statef_onlink)
- ia6->ia_flags |= IFA_ROUTE;
+ if (rt != NULL) {
+ if (rt->rt_refcnt <= 0) {
+ /* XXX: we should free the entry ourselves. */
+ rt->rt_refcnt++;
+ rtfree(rt);
+ }
}
+
+ return(error);
}
static struct in6_ifaddr *
-in6_ifadd(ifp, in6, addr, prefixlen)
- struct ifnet *ifp;
- struct in6_addr *in6;
- struct in6_addr *addr;
- int prefixlen; /* prefix len of the new prefix in "in6" */
+in6_ifadd(pr)
+ struct nd_prefix *pr;
{
+ struct ifnet *ifp = pr->ndpr_ifp;
struct ifaddr *ifa;
- struct in6_ifaddr *ia, *ib, *oia;
- int s, error;
+ struct in6_aliasreq ifra;
+ struct in6_ifaddr *ia, *ib;
+ int error, plen0;
struct in6_addr mask;
+ int prefixlen = pr->ndpr_plen;
in6_prefixlen2mask(&mask, prefixlen);
- /* find link-local address (will be interface ID) */
- ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */
+ /*
+ * find a link-local address (will be interface ID).
+ * Is it really mandatory? Theoretically, a global or a site-local
+ * address can be configured without a link-local address, if we
+ * have a unique interface identifier...
+ *
+ * it is not mandatory to have a link-local address, we can generate
+ * interface identifier on the fly. we do this because:
+ * (1) it should be the easiest way to find interface identifier.
+ * (2) RFC2462 5.4 suggesting the use of the same interface identifier
+ * for multiple addresses on a single interface, and possible shortcut
+ * of DAD. we omitted DAD for this reason in the past.
+ * (3) a user can prevent autoconfiguration of global address
+ * by removing link-local address by hand (this is partly because we
+ * don't have other way to control the use of IPv6 on a interface.
+ * this has been our design choice - cf. NRL's "ifconfig auto").
+ * (4) it is easier to manage when an interface has addresses
+ * with the same interface identifier, than to have multiple addresses
+ * with different interface identifiers.
+ */
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */
if (ifa)
ib = (struct in6_ifaddr *)ifa;
else
@@ -1228,199 +1814,72 @@ in6_ifadd(ifp, in6, addr, prefixlen)
#endif
/* prefixlen + ifidlen must be equal to 128 */
- if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) {
- nd6log((LOG_ERR, "in6_ifadd: wrong prefixlen for %s"
- "(prefix=%d ifid=%d)\n", ifp->if_xname,
- prefixlen,
- 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr)));
+ plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL);
+ if (prefixlen != plen0) {
+ nd6log((LOG_INFO, "in6_ifadd: wrong prefixlen for %s "
+ "(prefix=%d ifid=%d)\n",
+ ifp->if_xname, prefixlen, 128 - plen0));
return NULL;
}
/* make ifaddr */
- ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT);
- if (ia == NULL) {
- printf("ENOBUFS in in6_ifadd %d\n", __LINE__);
- return NULL;
- }
- bzero((caddr_t)ia, sizeof(*ia));
- ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
- 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;
-
- /* link to in6_ifaddr */
- if ((oia = in6_ifaddr) != NULL) {
- for( ; oia->ia_next; oia = oia->ia_next)
- continue;
- oia->ia_next = ia;
- } 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 */
- 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);
- ia->ia_addr.sin6_family = AF_INET6;
+ bzero(&ifra, sizeof(ifra));
+ /*
+ * in6_update_ifa() does not use ifra_name, but we accurately set it
+ * for safety.
+ */
+ strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name));
+ ifra.ifra_addr.sin6_family = AF_INET6;
+ ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
/* prefix */
- bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr));
- ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
- ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
- ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
- ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
- /* interface ID */
- ia->ia_addr.sin6_addr.s6_addr32[0]
- |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
- ia->ia_addr.sin6_addr.s6_addr32[1]
- |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
- ia->ia_addr.sin6_addr.s6_addr32[2]
- |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
- ia->ia_addr.sin6_addr.s6_addr32[3]
- |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
-
- /* new prefix */
- ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
- ia->ia_prefixmask.sin6_family = AF_INET6;
- bcopy(&mask, &ia->ia_prefixmask.sin6_addr,
- sizeof(ia->ia_prefixmask.sin6_addr));
-
- /* same routine */
- ia->ia_ifa.ifa_rtrequest =
- (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest;
- ia->ia_ifa.ifa_flags |= RTF_CLONING;
- ia->ia_ifa.ifa_metric = ifp->if_metric;
-
- /* add interface route */
- if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) {
- nd6log((LOG_NOTICE,
- "in6_ifadd: failed to add an interface route "
- "for %s/%d on %s, errno = %d\n",
- ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen,
- ifp->if_xname, error));
- }
- else
- ia->ia_flags |= IFA_ROUTE;
-
- *addr = ia->ia_addr.sin6_addr;
-
- if (ifp->if_flags & IFF_MULTICAST) {
- int error; /* not used */
- struct in6_addr sol6;
+ bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr,
+ sizeof(ifra.ifra_addr.sin6_addr));
+ ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
+ ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
- /* Restore saved multicast addresses(if any). */
- in6_restoremkludge(ia, ifp);
-
- /* join solicited node multicast address */
- bzero(&sol6, sizeof(sol6));
- sol6.s6_addr16[0] = htons(0xff02);
- sol6.s6_addr16[1] = htons(ifp->if_index);
- sol6.s6_addr32[1] = 0;
- sol6.s6_addr32[2] = htonl(1);
- sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3];
- sol6.s6_addr8[12] = 0xff;
- (void)in6_addmulti(&sol6, ifp, &error);
- }
-
- ia->ia6_flags |= IN6_IFF_TENTATIVE;
+ /* interface ID */
+ ifra.ifra_addr.sin6_addr.s6_addr32[0] |=
+ (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[1] |=
+ (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
+ (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
+ (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
+
+ /* new prefix mask. */
+ ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ifra.ifra_prefixmask.sin6_family = AF_INET6;
+ bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr,
+ sizeof(ifra.ifra_prefixmask.sin6_addr));
/*
- * To make the interface up. Only AF_INET6 in ia is used...
+ * lifetime.
+ * XXX: in6_init_address_ltimes would override these values later.
+ * We should reconsider this logic.
*/
- s = splimp();
- if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) {
- splx(s);
- return NULL;
- }
- splx(s);
+ ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime;
+ ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime;
- /* Perform DAD, if needed. */
- nd6_dad_start((struct ifaddr *)ia, NULL);
+ /* XXX: scope zone ID? */
- return ia;
-}
+ ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */
-int
-in6_ifdel(ifp, in6)
- struct ifnet *ifp;
- struct in6_addr *in6;
-{
- struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL;
- struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL;
-
- if (!ifp)
- return -1;
+ /* allocate ifaddr structure, link into chain, etc. */
+ if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) {
+ nd6log((LOG_ERR,
+ "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n",
+ ip6_sprintf(&ifra.ifra_addr.sin6_addr), ifp->if_xname,
+ error));
+ return(NULL); /* ifaddr must not have been allocated. */
+ }
- ia = in6ifa_ifpwithaddr(ifp, in6);
- if (!ia)
- return -1;
+ ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr);
- if (ifp->if_flags & IFF_MULTICAST) {
- /*
- * delete solicited multicast addr for deleting host id
- */
- struct in6_multi *in6m;
- struct in6_addr llsol;
- bzero(&llsol, sizeof(struct in6_addr));
- llsol.s6_addr16[0] = htons(0xff02);
- llsol.s6_addr16[1] = htons(ifp->if_index);
- llsol.s6_addr32[1] = 0;
- llsol.s6_addr32[2] = htonl(1);
- llsol.s6_addr32[3] =
- ia->ia_addr.sin6_addr.s6_addr32[3];
- llsol.s6_addr8[12] = 0xff;
-
- IN6_LOOKUP_MULTI(llsol, ifp, in6m);
- if (in6m)
- in6_delmulti(in6m);
- }
-
- if (ia->ia_flags & IFA_ROUTE) {
- rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
- ia->ia_flags &= ~IFA_ROUTE;
- }
-
- TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list);
- IFAFREE(&ia->ia_ifa);
-
- /* lladdr is never deleted */
- oia = ia;
- if (oia == (ia = in6_ifaddr))
- in6_ifaddr = ia->ia_next;
- else {
- while (ia->ia_next && (ia->ia_next != oia))
- ia = ia->ia_next;
- if (ia->ia_next)
- ia->ia_next = oia->ia_next;
- else
- return -1;
- }
-
- in6_savemkludge(oia);
- IFAFREE((&oia->ia_ifa));
-/* xxx
- rtrequest(RTM_DELETE,
- (struct sockaddr *)&ia->ia_addr,
- (struct sockaddr *)0
- (struct sockaddr *)&ia->ia_prefixmask,
- RTF_UP|RTF_CLONING,
- (struct rtentry **)0);
-*/
- return 0;
+ return(ia); /* this is always non-NULL */
}
int
@@ -1428,7 +1887,7 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr)
{
long time_second = time.tv_sec;
- /* check if preferred lifetime > valid lifetime */
+ /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */
if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) {
nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime"
"(%d) is greater than valid lifetime(%d)\n",
@@ -1448,25 +1907,17 @@ in6_init_prefix_ltimes(struct nd_prefix *ndpr)
}
static void
-in6_init_address_ltimes(struct nd_prefix *new,
- struct in6_addrlifetime *lt6,
- int update_vltime)
+in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6)
{
long time_second = time.tv_sec;
/* 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_expire */
+ if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME)
+ lt6->ia6t_expire = 0;
+ else {
+ lt6->ia6t_expire = time_second;
+ lt6->ia6t_expire += lt6->ia6t_vltime;
}
/* init ia6t_preferred */
@@ -1476,10 +1927,6 @@ in6_init_address_ltimes(struct nd_prefix *new,
lt6->ia6t_preferred = time_second;
lt6->ia6t_preferred += lt6->ia6t_pltime;
}
- /* Ensure addr lifetime <= prefix lifetime. */
- if (new->ndpr_preferred && lt6->ia6t_preferred
- && new->ndpr_preferred < lt6->ia6t_preferred)
- lt6->ia6t_preferred = new->ndpr_preferred;
}
/*
@@ -1523,14 +1970,22 @@ rt6_deleteroute(rn, arg)
return(0);
/*
+ * Do not delete a static route.
+ * XXX: this seems to be a bit ad-hoc. Should we consider the
+ * 'cloned' bit instead?
+ */
+ if ((rt->rt_flags & RTF_STATIC) != 0)
+ return(0);
+
+ /*
* We delete only host route. This means, in particular, we don't
* delete default route.
*/
if ((rt->rt_flags & RTF_HOST) == 0)
return(0);
- return(rtrequest(RTM_DELETE, rt_key(rt),
- rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0));
+ return(rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
+ rt_mask(rt), rt->rt_flags, 0));
#undef SIN6
}
@@ -1545,21 +2000,15 @@ nd6_setdefaultiface(ifindex)
if (nd6_defifindex != ifindex) {
nd6_defifindex = ifindex;
- if (nd6_defifindex > 0)
+ if (nd6_defifindex > 0) {
nd6_defifp = ifindex2ifnet[nd6_defifindex];
- else
+ } 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.
+ * Rescan default router list, refresh default route(s).
*/
- if (TAILQ_FIRST(&nd_defrouter) == NULL)
- defrouter_select();
+ defrouter_select();
}
return(error);