diff options
-rw-r--r-- | sys/net/if_bridge.c | 101 | ||||
-rw-r--r-- | sys/net/if_lagg.c | 30 | ||||
-rw-r--r-- | sys/netinet6/in6.c | 26 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.c | 15 | ||||
-rw-r--r-- | sys/netinet6/in6_var.h | 1 | ||||
-rw-r--r-- | sys/netinet6/nd6.c | 24 |
6 files changed, 164 insertions, 33 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index bc9bda09..3c4f12c 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -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, void *arg) 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, void *arg) /* 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, void *arg) 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, struct mbuf *m, struct ether_header *eh, continue; } bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN); - } else + } else m_freem(m); } diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c index 11273b2..9867c97 100644 --- a/sys/net/if_lagg.c +++ b/sys/net/if_lagg.c @@ -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, struct ifnet *ifp) 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; diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 80359c1..9e005c5 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1987,6 +1987,32 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr) } /* + * 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. */ diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 85890ab..674356a 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -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 ifnet *altifp) 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 ifnet *altifp) * 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 ifnet *altifp) * 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 ifnet *altifp) /* * 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 ifnet *altifp) 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; diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 3b4e89e..b3f7218 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -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 *); diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 0e30825..0a67a8a 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -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; |