Some follow-up fixes for IFID collision handling in IPv6CP.
authorstsp <stsp@openbsd.org>
Tue, 7 Jan 2014 16:34:05 +0000 (16:34 +0000)
committerstsp <stsp@openbsd.org>
Tue, 7 Jan 2014 16:34:05 +0000 (16:34 +0000)
Really change the link-local address in the unlikely event of an IFID
collision, instead of going into an infinite conf-nak loop with the peer.

To make the netinet6 code use the IPv6CP IFID in a new link-local address,
in6_ifattach_linklocal() must accept a provided IFID.  Replace the unused
'altifp' parameter with a new 'ifid' parameter for this purpose.

Always use the latest suggested address in IPv6CP replies, even if
the task to update the interface's address hasn't run yet.
Also, clear the ifindex (KAME hack) in addresses sent during IPv6CP.

ok mpi

sys/net/if_spppsubr.c
sys/netinet6/in6.c
sys/netinet6/in6_ifattach.c
sys/netinet6/in6_ifattach.h

index 59de883..b080aa4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_spppsubr.c,v 1.114 2013/12/11 18:27:23 jca Exp $   */
+/*     $OpenBSD: if_spppsubr.c,v 1.115 2014/01/07 16:34:05 stsp Exp $  */
 /*
  * Synchronous PPP/Cisco link level subroutines.
  * Keepalive protocol implemented in both Cisco and PPP modes.
 #include <netinet/if_ether.h>
 #endif
 
+#ifdef INET6
+#include <netinet6/in6_ifattach.h>
+#endif
+
 #include <net/if_sppp.h>
 
 # define UNTIMEOUT(fun, arg, handle)   \
@@ -3222,7 +3226,10 @@ sppp_ipv6cp_RCR(struct sppp *sp, struct lcp_header *h, int len)
                addlog("\n");
 
        /* pass 2: parse option values */
-       sppp_get_ip6_addrs(sp, &myaddr, NULL, NULL);
+       if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN)
+               myaddr = sp->ipv6cp.req_ifid.ifra_addr.sin6_addr;
+       else
+               sppp_get_ip6_addrs(sp, &myaddr, NULL, NULL);
        if (debug)
                log(LOG_DEBUG, "%s: ipv6cp parse opt values: ",
                       SPP_ARGS(ifp));
@@ -3455,7 +3462,10 @@ sppp_ipv6cp_scr(struct sppp *sp)
        int i = 0;
 
        if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_IFID)) {
-               sppp_get_ip6_addrs(sp, &ouraddr, NULL, NULL);
+               if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN)
+                       ouraddr = sp->ipv6cp.req_ifid.ifra_addr.sin6_addr;
+               else
+                       sppp_get_ip6_addrs(sp, &ouraddr, NULL, NULL);
                opt[i++] = IPV6CP_OPT_IFID;
                opt[i++] = 10;
                bcopy(&ouraddr.s6_addr[8], &opt[i], 8);
@@ -4768,6 +4778,25 @@ sppp_update_ip6_addr(void *arg1, void *arg2)
                return;
        }
 
+       /* 
+        * Changing the link-local address requires purging all
+        * existing addresses and routes for the interface first.
+        */
+       if (sp->ipv6cp.flags & IPV6CP_MYIFID_DYN) {
+               in6_ifdetach(ifp);
+               error = in6_ifattach_linklocal(ifp, &ifra->ifra_addr.sin6_addr);
+               if (error)
+                       log(LOG_ERR, SPP_FMT
+                           "could not update IPv6 address (error %d)\n",
+                           SPP_ARGS(ifp), error);
+               splx(s);
+               return;
+       }
+
+       /* 
+        * Code below changes address parameters only, not the address itself.
+        */
+
        /* Destination address can only be set for /128. */
        if (!in6_are_prefix_equal(&ia->ia_prefixmask.sin6_addr, &mask, 128)) {
                ifra->ifra_dstaddr.sin6_len = 0;
@@ -4843,6 +4872,7 @@ sppp_suggest_ip6_addr(struct sppp *sp, struct in6_addr *suggest)
                myaddr.s6_addr[14] ^= (random & 0xff);
                myaddr.s6_addr[15] ^= ((random & 0xff00) >> 8);
        }
+       myaddr.s6_addr16[1] = 0; /* KAME hack: clear ifindex */
        bcopy(&myaddr, suggest, sizeof(myaddr));
 }
 #endif /*INET6*/
index d9a472f..80c3571 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in6.c,v 1.126 2013/11/28 10:16:44 mpi Exp $   */
+/*     $OpenBSD: in6.c,v 1.127 2014/01/07 16:34:05 stsp Exp $  */
 /*     $KAME: in6.c,v 1.372 2004/06/14 08:14:21 itojun Exp $   */
 
 /*
@@ -2220,7 +2220,7 @@ in6_if_up(struct ifnet *ifp)
        /*
         * special cases, like 6to4, are handled in in6_ifattach
         */
-       in6_ifattach(ifp, NULL);
+       in6_ifattach(ifp);
 
        dad_delay = 0;
        TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
index 4a558bd..26369b0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in6_ifattach.c,v 1.65 2014/01/06 13:01:20 stsp Exp $  */
+/*     $OpenBSD: in6_ifattach.c,v 1.66 2014/01/07 16:34:05 stsp Exp $  */
 /*     $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $  */
 
 /*
@@ -67,7 +67,7 @@ int ip6_auto_linklocal = 1;   /* enable by default */
 
 int get_last_resort_ifid(struct ifnet *, struct in6_addr *);
 int get_hw_ifid(struct ifnet *, struct in6_addr *);
-int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
+int get_ifid(struct ifnet *, struct in6_addr *);
 int in6_ifattach_loopback(struct ifnet *);
 
 #define EUI64_GBIT     0x01
@@ -257,11 +257,9 @@ found:
  * Get interface identifier for the specified interface.  If it is not
  * available on ifp0, borrow interface identifier from other information
  * sources.
- *
- * altifp - secondary EUI64 source
  */
 int
-get_ifid(struct ifnet *ifp0, struct ifnet *altifp, struct in6_addr *in6)
+get_ifid(struct ifnet *ifp0, struct in6_addr *in6)
 {
        struct ifnet *ifp;
 
@@ -272,13 +270,6 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, struct in6_addr *in6)
                goto success;
        }
 
-       /* try secondary EUI64 source. this basically is for ATM PVC */
-       if (altifp && get_hw_ifid(altifp, in6) == 0) {
-               nd6log((LOG_DEBUG, "%s: got interface identifier from %s\n",
-                   ifp0->if_xname, altifp->if_xname));
-               goto success;
-       }
-
        /* next, try to get it from some other hardware interface */
        TAILQ_FOREACH(ifp, &ifnet, if_list) {
                if (ifp == ifp0)
@@ -318,11 +309,11 @@ success:
 }
 
 /*
- * altifp - secondary EUI64 source
+ * ifid - used as EUI64 if not NULL, overrides other EUI64 sources
  */
 
 int
-in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp)
+in6_ifattach_linklocal(struct ifnet *ifp, struct in6_addr *ifid)
 {
        struct in6_ifaddr *ia;
        struct in6_aliasreq ifra;
@@ -348,8 +339,15 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp)
        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 (ifid) {
+               ifra.ifra_addr.sin6_addr = *ifid;
+               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;
+               ifra.ifra_addr.sin6_addr.s6_addr[8] &= ~EUI64_GBIT;
+               ifra.ifra_addr.sin6_addr.s6_addr[8] |= EUI64_UBIT;
        } else {
-               if (get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr) != 0) {
+               if (get_ifid(ifp, &ifra.ifra_addr.sin6_addr) != 0) {
                        nd6log((LOG_ERR,
                            "%s: no ifid available\n", ifp->if_xname));
                        return (-1);
@@ -565,11 +563,9 @@ in6_nigroup(struct ifnet *ifp, const char *name, int namelen,
  * XXX multiple loopback interface needs more care.  for instance,
  * nodelocal address needs to be configured onto only one of them.
  * XXX multiple link-local address case
- *
- * altifp - secondary EUI64 source
  */
 void
-in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
+in6_ifattach(struct ifnet *ifp)
 {
        struct in6_ifaddr *ia;
        struct in6_addr in6;
@@ -634,7 +630,7 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
        if (ip6_auto_linklocal) {
                ia = in6ifa_ifpforlinklocal(ifp, 0);
                if (ia == NULL) {
-                       if (in6_ifattach_linklocal(ifp, altifp) == 0) {
+                       if (in6_ifattach_linklocal(ifp, NULL) == 0) {
                                /* linklocal address assigned */
                        } else {
                                /* failed to assign linklocal address. bark? */
index 44bea78..6f574d6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in6_ifattach.h,v 1.5 2006/08/31 12:37:31 mcbride Exp $        */
+/*     $OpenBSD: in6_ifattach.h,v 1.6 2014/01/07 16:34:05 stsp Exp $   */
 /*     $KAME: in6_ifattach.h,v 1.9 2000/04/12 05:35:48 itojun Exp $    */
 
 /*
 #define _NETINET6_IN6_IFATTACH_H_
 
 #ifdef _KERNEL
-void in6_ifattach(struct ifnet *, struct ifnet *);
+void in6_ifattach(struct ifnet *);
 void in6_ifdetach(struct ifnet *);
 int in6_nigroup(struct ifnet *, const char *, int, struct sockaddr_in6 *);
-int in6_ifattach_linklocal(struct ifnet *, struct ifnet *);
+int in6_ifattach_linklocal(struct ifnet *, struct in6_addr *);
 #endif /* _KERNEL */
 
 #endif /* _NETINET6_IN6_IFATTACH_H_ */