this is not a request for oks, this is me backing up a hack i did to fix a problem i needed a quick solution for.
we're in the process of moving a bunch of boxes to a new site, and are lucky that we can take our address space with us. to let us gradually migrate hosts i want to stretch the layer 2 network between sites, but i only have ip connectivity between them. therefore i'm bridging vlans over etherip. i also care about having some redundancy during this process (which is going to take weeks at a minimum), so i have a pair of boxes at each site act as endpoints to the bridges. to keep it simple im using carp on each end to elect the active bridge endpoint on each side. this means only one of a pair rxes the etherip packets from the remote site to be forwarded into the DC the pair sit in. unfortunately i discovered that carp has no influence on sending etherip to the remote DC. once i had both pairs of bridges set up i managed to introduce a loop at layer 2 which DoSsed both DCs. in more detail, say I have DCs A and B, and each site has 2 bridge boxes that are called A0, A1, B0, and B1. A0 and B0 are the carp masters. say A0 rxes a broadcast packet from a vlan on its physical interface. it will forward that to B0, which transmits it to it's physical interface. B1 will rx the broadcast packet and send it to DC A via A0. A0 pushes the broadcast packet to it's physical interface, which is rxed by B1. B1 pushes it to A0, and so on. my quick and dirty hack is to make transmission over the etherip leg of a bridge depend on the state of the carp interface. this is implemented by hacking up etherip so it supports the configuration of a parent interface. etherip then watches whether the parent is running and has link. so i have the following config in DC A: xdlg@dca-bridge0 ~$ sudo cat /etc/hostname.carp423 carpdev vlan423 vhid 23 pass secret inet 172.23.84.113 255.255.255.248 NONE xdlg@dca-bridge0 ~$ sudo cat /etc/hostname.etherip0 tunnel 172.23.84.113 172.23.84.121 parent carp423 up this sits underneath this: xdlg@dca-bridge0 ~$ for i in vlan374 vlan10374 bridge374; do ifconfig $i; done vlan374: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 9000 lladdr 00:50:56:a1:d7:f7 description: labs-servers index 29 priority 0 llprio 3 encap: vnetid 374 parent em0 txprio packet rxprio outer groups: vlan overlay media: Ethernet autoselect (1000baseT full-duplex,master) status: active vlan10374: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 9000 lladdr fe:e1:ba:d0:3f:da description: labs-servers index 30 priority 0 llprio 3 encap: vnetid 374 parent etherip0 txprio packet rxprio outer groups: vlan overlay media: Ethernet autoselect status: active bridge374: flags=41<UP,RUNNING> description: labs-servers index 31 llprio 3 groups: bridge priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp designated: id 00:00:00:00:00:00 priority 0 vlan10374 flags=2007<LEARNING,DISCOVER,BLOCKNONIP> port 30 ifpriority 0 ifcost 0 vlan374 flags=2007<LEARNING,DISCOVER,BLOCKNONIP> port 29 ifpriority 0 ifcost 0 Addresses (max cache: 100, timeout: 240): systat if on dca-bridge0 has this: IFACE STATE DESC IPKTS IBYTES IFAILS OPKTS OBYTES OFAILS COLLS em0 up:U 1505 276472 0 7 1066 0 0 enc0 dn:U 0 0 0 0 0 0 0 lo0 up 0 0 0 0 0 0 0 carp423 up:D 4 252 0 0 0 0 0 etherip0 up:D 0 0 0 10 914 10 0 vlan374 up:U labs-servers 0 0 0 0 0 0 0 vlan10374 up:D labs-servers 0 0 0 0 0 0 0 bridge374 up labs-servers 0 0 0 0 0 0 0 and dca-bridge1: IFACE STATE DESC IPKTS IBYTES IFAILS OPKTS OBYTES OFAILS COLLS em0 up:U 841 140375 0 45 6265 0 0 enc0 dn:U 0 0 0 0 0 0 0 lo0 up 0 0 0 0 0 0 0 carp423 up:U 2 126 1 3 126 0 0 etherip0 up:U 8 1242 0 27 2751 0 0 vlan374 up:U labs-servers 20 4755 0 8 3861 0 0 vlan10374 up:U labs-servers 10 3981 0 12 3499 0 0 bridge374 up labs-servers 26 8496 0 20 7360 0 0 is there something obvious im missing here? how else do i make the backup bridge not transmit to the other DC? Index: if_etherip.c =================================================================== RCS file: /cvs/src/sys/net/if_etherip.c,v retrieving revision 1.45 diff -u -p -r1.45 if_etherip.c --- if_etherip.c 23 Apr 2019 10:53:45 -0000 1.45 +++ if_etherip.c 16 Jul 2019 09:50:34 -0000 @@ -90,6 +90,10 @@ struct etherip_softc { int sc_rxhprio; uint16_t sc_df; uint8_t sc_ttl; + + unsigned int sc_parent; + void *sc_lcookie; + void *sc_dcookie; }; /* @@ -115,6 +119,12 @@ int etherip_down(struct etherip_softc *) struct etherip_softc *etherip_find(const struct etherip_tunnel *); int etherip_input(struct etherip_tunnel *, struct mbuf *, uint8_t, int); +static int etherip_get_parent(struct etherip_softc *, struct if_parent *); +static int etherip_set_parent(struct etherip_softc *, + const struct if_parent *); +static void etherip_del_parent(void *); +static void etherip_parent_link(void *); + struct if_clone etherip_cloner = IF_CLONE_INITIALIZER("etherip", etherip_clone_create, etherip_clone_destroy); @@ -176,6 +186,8 @@ etherip_clone_destroy(struct ifnet *ifp) struct etherip_softc *sc = ifp->if_softc; NET_LOCK(); + etherip_del_parent(sc); + if (ISSET(ifp->if_flags, IFF_RUNNING)) etherip_down(sc); @@ -214,6 +226,11 @@ etherip_start(struct ifnet *ifp) caddr_t if_bpf; #endif + if (ifp->if_link_state == LINK_STATE_DOWN) { + ifq_purge(&ifp->if_snd); + return; + } + while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) { #if NBPFILTER > 0 if_bpf = ifp->if_bpf; @@ -341,6 +358,19 @@ etherip_ioctl(struct ifnet *ifp, u_long case SIOCDELMULTI: break; + case SIOCGIFPARENT: + error = etherip_get_parent(sc, (struct if_parent *)data); + break; + + case SIOCSIFPARENT: + error = etherip_set_parent(sc, (struct if_parent *)data); + break; + + case SIOCDIFPARENT: + etherip_del_parent(sc); + error = 0; + break; + default: error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); break; @@ -352,6 +382,98 @@ etherip_ioctl(struct ifnet *ifp, u_long } return (error); +} + +static int +etherip_get_parent(struct etherip_softc *sc, struct if_parent *parent) +{ + struct ifnet *ifp0; + + ifp0 = if_get(sc->sc_parent); + if (ifp0 == NULL) + return (ENOTTY); /* invisible to ifconfig until set */ + + if (strlcpy(parent->ifp_parent, ifp0->if_xname, + sizeof(parent->ifp_parent)) > sizeof(parent->ifp_parent)) + return (EINVAL); /* XXX */ + + if_put(ifp0); + + return (0); +} + +static void +etherip_parent_link(void *arg) +{ + struct etherip_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ifnet *ifp0; + u_char link_state = LINK_STATE_UNKNOWN; + + ifp0 = if_get(sc->sc_parent); + if (ifp0 != NULL) { + link_state = (ISSET(ifp0->if_flags, IFF_RUNNING) && + LINK_STATE_IS_UP(ifp0->if_link_state)) ? + LINK_STATE_FULL_DUPLEX : LINK_STATE_DOWN; + } + if_put(ifp0); + + if (ifp->if_link_state != link_state) { + ifp->if_link_state = link_state; + if_link_state_change(ifp); + } +} + +static int +etherip_set_parent(struct etherip_softc *sc, const struct if_parent *parent) +{ + struct ifnet *ifp0; + + NET_ASSERT_LOCKED(); + + ifp0 = ifunit(parent->ifp_parent); /* no ref */ + if (ifp0 == NULL) + return (EINVAL); + + if (sc->sc_parent == ifp0->if_index) + return (0); /* nop */ + + sc->sc_parent = ifp0->if_index; + + sc->sc_lcookie = hook_establish(ifp0->if_linkstatehooks, 1, + etherip_parent_link, sc); + + sc->sc_dcookie = hook_establish(ifp0->if_detachhooks, 0, + etherip_del_parent, sc); + + etherip_parent_link(sc); + + return (0); +} + +static void +etherip_del_parent(void *arg) +{ + struct etherip_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ifnet *ifp0; + u_char link_state = LINK_STATE_UNKNOWN; + + NET_ASSERT_LOCKED(); + + ifp0 = if_get(sc->sc_parent); + if (ifp0 != NULL) { + hook_disestablish(ifp0->if_detachhooks, sc->sc_dcookie); + hook_disestablish(ifp0->if_linkstatehooks, sc->sc_lcookie); + } + if_put(ifp0); + + sc->sc_parent = 0; + + if (ifp->if_link_state != link_state) { + ifp->if_link_state = link_state; + if_link_state_change(ifp); + } } int