summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6_ifattach.c11
-rw-r--r--sys/netinet6/in6_ifattach.h1
-rw-r--r--sys/netinet6/nd6_nbr.c41
3 files changed, 45 insertions, 8 deletions
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index d9244dc..1ddf5fe 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -75,7 +75,6 @@ extern struct inpcbinfo ripcbinfo;
static int get_rand_ifid __P((struct ifnet *, struct in6_addr *));
static int generate_tmp_ifid __P((u_int8_t *, const u_int8_t *, u_int8_t *));
-static int get_hw_ifid __P((struct ifnet *, struct in6_addr *));
static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *));
static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *));
static int in6_ifattach_loopback __P((struct ifnet *));
@@ -217,8 +216,8 @@ generate_tmp_ifid(seed0, seed1, ret)
* Get interface identifier for the specified interface.
* XXX assumes single sockaddr_dl (AF_LINK address) per an interface
*/
-static int
-get_hw_ifid(ifp, in6)
+int
+in6_get_hw_ifid(ifp, in6)
struct ifnet *ifp;
struct in6_addr *in6; /* upper 64bits are preserved */
{
@@ -359,14 +358,14 @@ get_ifid(ifp0, altifp, in6)
struct ifnet *ifp;
/* first, try to get it from the interface itself */
- if (get_hw_ifid(ifp0, in6) == 0) {
+ if (in6_get_hw_ifid(ifp0, in6) == 0) {
nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n",
if_name(ifp0)));
goto success;
}
/* try secondary EUI64 source. this basically is for ATM PVC */
- if (altifp && get_hw_ifid(altifp, in6) == 0) {
+ if (altifp && in6_get_hw_ifid(altifp, in6) == 0) {
nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n",
if_name(ifp0), if_name(altifp)));
goto success;
@@ -377,7 +376,7 @@ get_ifid(ifp0, altifp, in6)
for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) {
if (ifp == ifp0)
continue;
- if (get_hw_ifid(ifp, in6) != 0)
+ if (in6_get_hw_ifid(ifp, in6) != 0)
continue;
/*
diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h
index e7fbc89..c91f3ff 100644
--- a/sys/netinet6/in6_ifattach.h
+++ b/sys/netinet6/in6_ifattach.h
@@ -38,6 +38,7 @@ void in6_ifattach __P((struct ifnet *, struct ifnet *));
void in6_ifdetach __P((struct ifnet *));
void in6_get_tmpifid __P((struct ifnet *, u_int8_t *, const u_int8_t *, int));
void in6_tmpaddrtimer __P((void *));
+int in6_get_hw_ifid __P((struct ifnet *, struct in6_addr *));
int in6_nigroup __P((struct ifnet *, const char *, int, struct in6_addr *));
#endif /* _KERNEL */
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index e792443..8a97f6e 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -57,6 +57,7 @@
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/in6_var.h>
+#include <netinet6/in6_ifattach.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h>
@@ -1306,6 +1307,7 @@ nd6_dad_duplicated(ifa)
struct ifaddr *ifa;
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
+ struct ifnet *ifp;
struct dadq *dp;
dp = nd6_dad_find(ifa);
@@ -1325,10 +1327,45 @@ nd6_dad_duplicated(ifa)
/* We are done with DAD, with duplicate address found. (failure) */
nd6_dad_stoptimer(dp);
+ ifp = ifa->ifa_ifp;
log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
- if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
+ if_name(ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
log(LOG_ERR, "%s: manual intervention required\n",
- if_name(ifa->ifa_ifp));
+ if_name(ifp));
+
+ /*
+ * If the address is a link-local address formed from an interface
+ * identifier based on the hardware address which is supposed to be
+ * uniquely assigned (e.g., EUI-64 for an Ethernet interface), IP
+ * operation on the interface SHOULD be disabled.
+ * [rfc2462bis-03 Section 5.4.5]
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) {
+ struct in6_addr in6;
+
+ /*
+ * To avoid over-reaction, we only apply this logic when we are
+ * very sure that hardware addresses are supposed to be unique.
+ */
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ATM:
+ case IFT_IEEE1394:
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ in6 = ia->ia_addr.sin6_addr;
+ if (in6_get_hw_ifid(ifp, &in6) == 0 &&
+ IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &in6)) {
+ ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
+ log(LOG_ERR, "%s: possible hardware address "
+ "duplication detected, disable IPv6\n",
+ if_name(ifp));
+ }
+ break;
+ }
+ }
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
OpenPOWER on IntegriCloud