Author: hrs
Date: Tue Jul  2 16:58:15 2013
New Revision: 252511
URL: http://svnweb.freebsd.org/changeset/base/252511

Log:
  - Allow ND6_IFF_AUTO_LINKLOCAL for IFT_BRIDGE.  An interface with IFT_BRIDGE
    is initialized with !ND6_IFF_AUTO_LINKLOCAL && !ND6_IFF_ACCEPT_RTADV
    regardless of net.inet6.ip6.accept_rtadv and net.inet6.ip6.auto_linklocal.
    To configure an autoconfigured link-local address (RFC 4862), the
    following rc.conf(5) configuration can be used:
  
     ifconfig_bridge0_ipv6="inet6 auto_linklocal"
  
  - if_bridge(4) now removes IPv6 addresses on a member interface to be
    added when the parent interface or one of the existing member
    interfaces has an IPv6 address.  if_bridge(4) merges each link-local
    scope zone which the member interfaces form respectively, so it causes
    address scope violation.  Removal of the IPv6 addresses prevents it.
  
  - if_lagg(4) now removes IPv6 addresses on a member interfaces
    unconditionally.
  
  - Set reasonable flags to non-IPv6-capable interfaces. [*]
  
  Submitted by: rpaulo [*]
  MFC after:    1 week

Modified:
  head/sys/net/if_bridge.c
  head/sys/net/if_lagg.c
  head/sys/netinet6/in6.c
  head/sys/netinet6/in6_ifattach.c
  head/sys/netinet6/in6_var.h
  head/sys/netinet6/nd6.c

Modified: head/sys/net/if_bridge.c
==============================================================================
--- head/sys/net/if_bridge.c    Tue Jul  2 16:39:12 2013        (r252510)
+++ head/sys/net/if_bridge.c    Tue Jul  2 16:58:15 2013        (r252511)
@@ -118,6 +118,7 @@ __FBSDID("$FreeBSD$");
 #ifdef INET6
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
+#include <netinet6/in6_ifattach.h>
 #endif
 #if defined(INET) || defined(INET6)
 #include <netinet/ip_carp.h>
@@ -1041,14 +1042,6 @@ bridge_ioctl_add(struct bridge_softc *sc
        if (ifs->if_bridge != NULL)
                return (EBUSY);
 
-       bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO);
-       if (bif == NULL)
-               return (ENOMEM);
-
-       bif->bif_ifp = ifs;
-       bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
-       bif->bif_savedcaps = ifs->if_capenable;
-
        switch (ifs->if_type) {
        case IFT_ETHER:
        case IFT_L2VLAN:
@@ -1056,20 +1049,94 @@ bridge_ioctl_add(struct bridge_softc *sc
                /* permitted interface types */
                break;
        default:
-               error = EINVAL;
-               goto out;
+               return (EINVAL);
        }
 
+#ifdef INET6
+       /*
+        * Two valid inet6 addresses with link-local scope must not be
+        * on the parent interface and the member interfaces at the
+        * same time.  This restriction is needed to prevent violation
+        * of link-local scope zone.  Attempts to add a member
+        * interface which has inet6 addresses when the parent has
+        * inet6 triggers removal of all inet6 addresses on the member
+        * interface.
+        */
+
+       /* Check if the parent interface has a link-local scope addr. */
+       if (in6ifa_llaonifp(sc->sc_ifp) != NULL) {
+               /*
+                * If any, remove all inet6 addresses from the member
+                * interfaces.
+                */
+               BRIDGE_XLOCK(sc);
+               LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+                       if (in6ifa_llaonifp(bif->bif_ifp)) {
+                               BRIDGE_UNLOCK(sc);
+                               in6_ifdetach(bif->bif_ifp);
+                               BRIDGE_LOCK(sc);
+                               if_printf(sc->sc_ifp,
+                                   "IPv6 addresses on %s have been removed "
+                                   "before adding it as a member to prevent "
+                                   "IPv6 address scope violation.\n",
+                                   bif->bif_ifp->if_xname);
+                       }
+               }
+               BRIDGE_XDROP(sc);
+               if (in6ifa_llaonifp(ifs)) {
+                       BRIDGE_UNLOCK(sc);
+                       in6_ifdetach(ifs);
+                       BRIDGE_LOCK(sc);
+                       if_printf(sc->sc_ifp,
+                           "IPv6 addresses on %s have been removed "
+                           "before adding it as a member to prevent "
+                           "IPv6 address scope violation.\n",
+                           ifs->if_xname);
+               }
+       } else {
+               struct in6_ifaddr *ia6_m, *ia6_s;
+               /*
+                * If not, check whether one of the existing member
+                * interfaces have inet6 address.  If any, remove
+                * inet6 addresses on the interface to be added.
+                */
+               BRIDGE_XLOCK(sc);
+               LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+                       ia6_m = in6ifa_llaonifp(bif->bif_ifp);
+                       if (ia6_m != NULL)
+                               break;
+               }
+               BRIDGE_XDROP(sc);
+               ia6_s = in6ifa_llaonifp(ifs);
+
+               if (ia6_m != NULL && ia6_s != NULL) {
+                       BRIDGE_UNLOCK(sc);
+                       in6_ifdetach(ifs);
+                       BRIDGE_LOCK(sc);
+                       if_printf(sc->sc_ifp, "IPv6 addresses on %s have "
+                                 "been removed before adding it as a member "
+                                 "to prevent IPv6 address scope violation.\n",
+                                 ifs->if_xname);
+               }
+       }
+#endif
        /* Allow the first Ethernet member to define the MTU */
        if (LIST_EMPTY(&sc->sc_iflist))
                sc->sc_ifp->if_mtu = ifs->if_mtu;
        else if (sc->sc_ifp->if_mtu != ifs->if_mtu) {
                if_printf(sc->sc_ifp, "invalid MTU: %lu(%s) != %lu\n",
                    ifs->if_mtu, ifs->if_xname, sc->sc_ifp->if_mtu);
-               error = EINVAL;
-               goto out;
+               return (EINVAL);
        }
 
+       bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO);
+       if (bif == NULL)
+               return (ENOMEM);
+
+       bif->bif_ifp = ifs;
+       bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
+       bif->bif_savedcaps = ifs->if_capenable;
+
        /*
         * Assign the interface's MAC address to the bridge if it's the first
         * member and the MAC address of the bridge has not been changed from
@@ -1104,12 +1171,10 @@ bridge_ioctl_add(struct bridge_softc *sc
                        BRIDGE_LOCK(sc);
                        break;
        }
-       if (error)
-               bridge_delete_member(sc, bif, 0);
-out:
+
        if (error) {
-               if (bif != NULL)
-                       free(bif, M_DEVBUF);
+               bridge_delete_member(sc, bif, 0);
+               free(bif, M_DEVBUF);
        }
        return (error);
 }
@@ -3408,7 +3473,7 @@ bridge_fragment(struct ifnet *ifp, struc
                                continue;
                        }
                        bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN);
-               } else 
+               } else
                        m_freem(m);
        }
 

Modified: head/sys/net/if_lagg.c
==============================================================================
--- head/sys/net/if_lagg.c      Tue Jul  2 16:39:12 2013        (r252510)
+++ head/sys/net/if_lagg.c      Tue Jul  2 16:58:15 2013        (r252511)
@@ -63,6 +63,8 @@ __FBSDID("$FreeBSD$");
 
 #ifdef INET6
 #include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/in6_ifattach.h>
 #endif
 
 #include <net/if_vlan_var.h>
@@ -543,6 +545,34 @@ lagg_port_create(struct lagg_softc *sc, 
        if (ifp->if_type != IFT_ETHER)
                return (EPROTONOSUPPORT);
 
+#ifdef INET6
+       /*
+        * The member interface should not have inet6 address because
+        * two interfaces with a valid link-local scope zone must not be
+        * merged in any form.  This restriction is needed to
+        * prevent violation of link-local scope zone.  Attempts to
+        * add a member interface which has inet6 addresses triggers
+        * removal of all inet6 addresses on the member interface.
+        */
+       SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
+               if (in6ifa_llaonifp(lp->lp_ifp)) {
+                       in6_ifdetach(lp->lp_ifp);
+                       if_printf(sc->sc_ifp,
+                           "IPv6 addresses on %s have been removed "
+                           "before adding it as a member to prevent "
+                           "IPv6 address scope violation.\n",
+                           lp->lp_ifp->if_xname);
+               }
+       }
+       if (in6ifa_llaonifp(ifp)) {
+               in6_ifdetach(ifp);
+               if_printf(sc->sc_ifp,
+                   "IPv6 addresses on %s have been removed "
+                   "before adding it as a member to prevent "
+                   "IPv6 address scope violation.\n",
+                   ifp->if_xname);
+       }
+#endif
        /* Allow the first Ethernet member to define the MTU */
        if (SLIST_EMPTY(&sc->sc_ports))
                sc->sc_ifp->if_mtu = ifp->if_mtu;

Modified: head/sys/netinet6/in6.c
==============================================================================
--- head/sys/netinet6/in6.c     Tue Jul  2 16:39:12 2013        (r252510)
+++ head/sys/netinet6/in6.c     Tue Jul  2 16:58:15 2013        (r252511)
@@ -1987,6 +1987,32 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, st
 }
 
 /*
+ * Find a link-local scoped address on ifp and return it if any.
+ */
+struct in6_ifaddr *
+in6ifa_llaonifp(struct ifnet *ifp)
+{
+       struct sockaddr_in6 *sin6;
+       struct ifaddr *ifa;
+
+       if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)
+               return (NULL);
+       if_addr_rlock(ifp);
+       TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+               if (ifa->ifa_addr->sa_family != AF_INET6)
+                       continue;
+               sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+               if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
+                   IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr) ||
+                   IN6_IS_ADDR_MC_NODELOCAL(&sin6->sin6_addr))
+                       break;
+       }
+       if_addr_runlock(ifp);
+
+       return ((struct in6_ifaddr *)ifa);
+}
+
+/*
  * Convert IP6 address to printable (loggable) representation. Caller
  * has to make sure that ip6buf is at least INET6_ADDRSTRLEN long.
  */

Modified: head/sys/netinet6/in6_ifattach.c
==============================================================================
--- head/sys/netinet6/in6_ifattach.c    Tue Jul  2 16:39:12 2013        
(r252510)
+++ head/sys/netinet6/in6_ifattach.c    Tue Jul  2 16:58:15 2013        
(r252511)
@@ -266,6 +266,7 @@ found:
 
        /* get EUI64 */
        switch (ifp->if_type) {
+       case IFT_BRIDGE:
        case IFT_ETHER:
        case IFT_L2VLAN:
        case IFT_FDDI:
@@ -727,6 +728,8 @@ in6_ifattach(struct ifnet *ifp, struct i
        switch (ifp->if_type) {
        case IFT_PFLOG:
        case IFT_PFSYNC:
+               ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
+               ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
                return;
        }
 
@@ -734,7 +737,6 @@ in6_ifattach(struct ifnet *ifp, struct i
         * quirks based on interface type
         */
        switch (ifp->if_type) {
-#ifdef IFT_STF
        case IFT_STF:
                /*
                 * 6to4 interface is a very special kind of beast.
@@ -742,8 +744,8 @@ in6_ifattach(struct ifnet *ifp, struct i
                 * linklocals for 6to4 interface, but there's no use and
                 * it is rather harmful to have one.
                 */
-               goto statinit;
-#endif
+               ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
+               break;
        default:
                break;
        }
@@ -777,8 +779,7 @@ in6_ifattach(struct ifnet *ifp, struct i
        /*
         * assign a link-local address, if there's none.
         */
-       if (ifp->if_type != IFT_BRIDGE &&
-           !(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
+       if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
            ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL) {
                int error;
 
@@ -795,10 +796,6 @@ in6_ifattach(struct ifnet *ifp, struct i
                        ifa_free(&ia->ia_ifa);
        }
 
-#ifdef IFT_STF                 /* XXX */
-statinit:
-#endif
-
        /* update dynamically. */
        if (V_in6_maxmtu < ifp->if_mtu)
                V_in6_maxmtu = ifp->if_mtu;

Modified: head/sys/netinet6/in6_var.h
==============================================================================
--- head/sys/netinet6/in6_var.h Tue Jul  2 16:39:12 2013        (r252510)
+++ head/sys/netinet6/in6_var.h Tue Jul  2 16:58:15 2013        (r252511)
@@ -800,6 +800,7 @@ void        in6_setmaxmtu(void);
 int    in6_if2idlen(struct ifnet *);
 struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int);
 struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, struct in6_addr *);
+struct in6_ifaddr *in6ifa_llaonifp(struct ifnet *);
 char   *ip6_sprintf(char *, const struct in6_addr *);
 int    in6_addr2zoneid(struct ifnet *, struct in6_addr *, u_int32_t *);
 int    in6_matchlen(struct in6_addr *, struct in6_addr *);

Modified: head/sys/netinet6/nd6.c
==============================================================================
--- head/sys/netinet6/nd6.c     Tue Jul  2 16:39:12 2013        (r252510)
+++ head/sys/netinet6/nd6.c     Tue Jul  2 16:58:15 2013        (r252511)
@@ -176,13 +176,25 @@ nd6_ifattach(struct ifnet *ifp)
 
        nd->flags = ND6_IFF_PERFORMNUD;
 
-       /* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL. */
-       if (V_ip6_auto_linklocal || (ifp->if_flags & IFF_LOOPBACK))
+       /* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL.
+        * XXXHRS: Clear ND6_IFF_AUTO_LINKLOCAL on an IFT_BRIDGE interface by
+        * default regardless of the V_ip6_auto_linklocal configuration to
+        * give a reasonable default behavior.
+        */
+       if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) ||
+           (ifp->if_flags & IFF_LOOPBACK))
                nd->flags |= ND6_IFF_AUTO_LINKLOCAL;
-
-       /* A loopback interface does not need to accept RTADV. */
-       if (V_ip6_accept_rtadv && !(ifp->if_flags & IFF_LOOPBACK))
-               nd->flags |= ND6_IFF_ACCEPT_RTADV;
+       /*
+        * A loopback interface does not need to accept RTADV.
+        * XXXHRS: Clear ND6_IFF_ACCEPT_RTADV on an IFT_BRIDGE interface by
+        * default regardless of the V_ip6_accept_rtadv configuration to
+        * prevent the interface from accepting RA messages arrived
+        * on one of the member interfaces with ND6_IFF_ACCEPT_RTADV.
+        */
+       if (V_ip6_accept_rtadv &&
+           !(ifp->if_flags & IFF_LOOPBACK) &&
+           (ifp->if_type != IFT_BRIDGE))
+                       nd->flags |= ND6_IFF_ACCEPT_RTADV;
        if (V_ip6_no_radr && !(ifp->if_flags & IFF_LOOPBACK))
                nd->flags |= ND6_IFF_NO_RADR;
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to