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