diff options
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6_ifattach.c | 11 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.h | 1 | ||||
-rw-r--r-- | sys/netinet6/nd6_nbr.c | 41 |
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); |