diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -476,12 +476,23 @@ } #ifdef VIMAGE +static void +vnet_if_return_one(struct ifnet *ifp) +{ + int found __diagused; + + if (ifc_vmove(ifp, ifp->if_home_vnet, NULL) == ENOTSUP) { + found = if_unlink_ifnet(ifp, true); + MPASS(found); + if_vmove(ifp, ifp->if_home_vnet); + } +} + static void vnet_if_return(const void *unused __unused) { struct ifnet *ifp, *nifp; struct ifnet **pending; - int found __diagused; int i; i = 0; @@ -505,17 +516,16 @@ /* Return all inherited interfaces to their parent vnets. */ CK_STAILQ_FOREACH_SAFE(ifp, &V_ifnet, if_link, nifp) { if (ifp->if_home_vnet != ifp->if_vnet) { - found = if_unlink_ifnet(ifp, true); - MPASS(found); - - pending[i++] = ifp; + if (if_try_ref(ifp)) + pending[i++] = ifp; } } IFNET_WUNLOCK(); for (int j = 0; j < i; j++) { sx_xlock(&ifnet_detach_sxlock); - if_vmove(pending[j], pending[j]->if_home_vnet); + vnet_if_return_one(pending[j]); + if_rele(pending[j]); sx_xunlock(&ifnet_detach_sxlock); } @@ -708,7 +718,6 @@ bool if_try_ref(struct ifnet *ifp) { - NET_EPOCH_ASSERT(); return (refcount_acquire_if_not_zero(&ifp->if_refcount)); } @@ -1350,8 +1359,8 @@ } sx_xlock(&ifnet_detach_sxlock); - /* Make sure the VNET is stable. */ - shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); + /* Make sure the VNET and interface are stable. */ + shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet) || (ifp->if_flags & IFF_DYING); if (shutdown) { sx_xunlock(&ifnet_detach_sxlock); CURVNET_RESTORE(); @@ -1360,6 +1369,14 @@ } CURVNET_RESTORE(); + error = ifc_vmove(ifp, pr->pr_vnet, NULL); + if (error != ENOTSUP) { + sx_xunlock(&ifnet_detach_sxlock); + CURVNET_RESTORE(); + prison_free(pr); + return (error); + } + found = if_unlink_ifnet(ifp, true); if (! found) { sx_xunlock(&ifnet_detach_sxlock); @@ -1424,6 +1441,15 @@ return (EBUSY); } + sx_xlock(&ifnet_detach_sxlock); + error = ifc_vmove(ifp, vnet_dst, NULL); + sx_xunlock(&ifnet_detach_sxlock); + if (error != ENOTSUP) { + CURVNET_RESTORE(); + prison_free(pr); + return (error); + } + /* Get interface back from child jail/vnet. */ found = if_unlink_ifnet(ifp, true); MPASS(found); @@ -4262,6 +4288,22 @@ return ((struct ifnet *)ifp)->if_capenable; } +char * +if_copydescr(if_t ifp) +{ + char *descrbuf = NULL; + + sx_xlock(&ifdescr_sx); + int len = ifp->if_description != NULL ? strlen(ifp->if_description) : 0; + if (len > 0) { + descrbuf = malloc(len + 1, M_IFDESCR, M_WAITOK | M_ZERO); + memcpy(descrbuf, ifp->if_description, len); + } + sx_xunlock(&ifdescr_sx); + + return (descrbuf); +} + void if_setdescr(if_t ifp, char *descrbuf) { diff --git a/sys/net/if_clone.h b/sys/net/if_clone.h --- a/sys/net/if_clone.h +++ b/sys/net/if_clone.h @@ -55,6 +55,8 @@ typedef int ifc_create_f(struct if_clone *ifc, char *name, size_t maxlen, struct ifc_data *ifd, struct ifnet **ifpp); typedef int ifc_destroy_f(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags); +typedef int ifc_vmove_f(struct if_clone *ifc, struct ifnet *ifp_src, + struct ifc_data *ifd, struct ifnet **ifpp); struct if_clone_addreq { uint16_t version; /* Always 0 for now */ @@ -64,12 +66,15 @@ ifc_match_f *match_f; ifc_create_f *create_f; ifc_destroy_f *destroy_f; + ifc_vmove_f *vmove_f; }; -#define IFC_F_NOGROUP 0x01 /* Creation flag: don't add unit group */ -#define IFC_F_AUTOUNIT 0x02 /* Creation flag: automatically select unit */ -#define IFC_F_SYSSPACE 0x04 /* Cloner callback: params pointer is in kernel memory */ -#define IFC_F_FORCE 0x08 /* Deletion flag: force interface deletion */ +#define IFC_F_NOGROUP 0x01 /* Creation flag: don't add unit group */ +#define IFC_F_AUTOUNIT 0x02 /* Creation flag: automatically select unit */ +#define IFC_F_SYSSPACE 0x04 /* Cloner callback: params pointer is in kernel memory */ +#define IFC_F_FORCE 0x08 /* Deletion flag: force interface deletion */ +#define IFC_F_NOMOVE 0x10 /* Creation flag: moving between vnets unsupported */ +#define IFC_F_MOVED 0x40 /* Deletion flag: indicate interface was moved */ #define IFC_NOGROUP IFC_F_NOGROUP @@ -82,6 +87,18 @@ bool ifc_unlink_ifp(struct if_clone *ifc, struct ifnet *ifp); int ifc_copyin(const struct ifc_data *ifd, void *target, size_t len); +int ifc_vmove(struct ifnet *ifp, struct vnet *vnet, struct ifnet **ifpp); + +struct ifc_vparam_data { + char ifname[IFNAMSIZ]; + int unit; + struct vnet *home_vnet; + char *ifdescr; + int mtu; +}; +void ifc_save_vparams(struct ifnet *ifp, struct ifc_vparam_data *vparams); +void ifc_apply_vparams(struct ifnet *ifp, struct ifc_vparam_data *vparams); + #ifdef CLONE_COMPAT_13 /* Methods. */ diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -51,6 +51,12 @@ #include #include +/* + * Interface is always linked to the ifc in the home vnet. + * Similarly, interface units are referenced in the home vnet + * + */ + /* Current IF_MAXUNIT expands maximum to 5 characters. */ #define IFCLOSIZ (IFNAMSIZ - 5) @@ -75,6 +81,9 @@ ifc_match_f *ifc_match; /* (c) Matcher function */ ifc_create_f *ifc_create; /* (c) Creates new interface */ ifc_destroy_f *ifc_destroy; /* (c) Destroys cloned interface */ +#ifdef VIMAGE + ifc_vmove_f *ifc_vmove; /* (c) Moves between vnets */ +#endif #ifdef CLONE_COMPAT_13 /* (c) Driver specific cloning functions. Called with no locks held. */ @@ -105,6 +114,8 @@ static void if_clone_free(struct if_clone *ifc); static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, struct ifc_data *ifd, struct ifnet **ifpp); +static int if_clone_destroyif_flags(struct if_clone *ifc, struct ifnet *ifp, + uint32_t flags); static int ifc_simple_match(struct if_clone *ifc, const char *name); static int ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit); @@ -282,6 +293,106 @@ return (ifc); } +#ifdef VIMAGE +/* + * "Default" vmove handler deleting and re-creating interface + * without any creation params + */ +static int +if_vmove_simple(struct if_clone *ifc, struct ifnet *ifp_src, struct ifc_data *ifd, + struct ifnet **ifpp) +{ + struct ifc_vparam_data vp = {}; + int error; + + ifc_save_vparams(ifp_src, &vp); + + CURVNET_SET_QUIET(ifd->vnet); + error = (*ifc->ifc_create)(ifc, vp.ifname, sizeof(vp.ifname), ifd, ifpp); + ifc_apply_vparams(*ifpp, &vp); + CURVNET_RESTORE(); + + if (error == 0) + error = if_clone_destroyif_flags(ifc, ifp_src, IFC_F_MOVED); + + return (error); +} + +void +ifc_save_vparams(struct ifnet *ifp, struct ifc_vparam_data *ivd) +{ + bzero(ivd, sizeof(*ivd)); + + strlcpy(ivd->ifname, if_name(ifp), sizeof(ivd->ifname)); + ivd->unit = ifp->if_dunit; + ivd->home_vnet = ifp->if_home_vnet; + ivd->ifdescr = if_copydescr(ifp); + ivd->mtu = if_getmtu(ifp); +} + +void +ifc_apply_vparams(struct ifnet *ifp, struct ifc_vparam_data *vp) +{ + if (ifp == NULL) { + if (vp->ifdescr != NULL) + if_freedescr(vp->ifdescr); + return; + } + + if (vp->home_vnet != NULL) + ifp->if_home_vnet = vp->home_vnet; + if (vp->ifdescr != NULL) + if_setdescr(ifp, vp->ifdescr); + if (vp->mtu > 0) + if_setmtu(ifp, vp->mtu); +} + +int +ifc_vmove(struct ifnet *ifp, struct vnet *vnet, + struct ifnet **ifpp) +{ + struct ifnet *ifp_dst = NULL; + int error = 0; + + sx_assert(&ifnet_detach_sxlock, SA_XLOCKED); + + if (!if_try_ref(ifp)) + return (EINVAL); + + struct if_clone *ifc = ifc_find_cloner(ifp->if_dname, ifp->if_home_vnet); + + /* Check if the interface with this name exists in target vnet first */ + CURVNET_SET_QUIET(vnet); + bool found = ifunit(if_name(ifp)) != NULL; + + if (found) + error = EEXIST; + if (ifc == NULL || (ifc->ifc_flags & IFC_F_NOMOVE)) + error = ENOTSUP; + + if (error == 0) { + struct ifc_data ifd = { + .vnet = vnet, + .flags = IFC_F_MOVED, + .unit = ifp->if_dunit, + }; + CURVNET_SET_QUIET(ifp->if_vnet); + error = ifc->ifc_vmove(ifc, ifp, &ifd, &ifp_dst); + CURVNET_RESTORE(); + } + if_rele(ifp); + + if (error == 0) + ifc_link_ifp(ifc, ifp_dst); + CURVNET_RESTORE(); + + if (ifpp != NULL) + *ifpp = ifp_dst; + + return (error); +} +#endif + /* * Create a clone network interface. */ @@ -290,11 +401,13 @@ struct ifc_data *ifd, struct ifnet **ifpp) { int err, unit = 0; + bool need_unit; if (ifunit(name) != NULL) return (EEXIST); - if (ifc->ifc_flags & IFC_F_AUTOUNIT) { + need_unit = ((ifc->ifc_flags & IFC_F_AUTOUNIT) && (!(ifd->flags & IFC_F_MOVED))); + if (need_unit) { if ((err = ifc_handle_unit(ifc, name, len, &unit)) != 0) return (err); ifd->unit = unit; @@ -305,7 +418,7 @@ if (err == 0) { MPASS(*ifpp != NULL); if_clone_addif(ifc, *ifpp); - } else if (ifc->ifc_flags & IFC_F_AUTOUNIT) + } else if (need_unit) ifc_free_unit(ifc, unit); return (err); @@ -344,6 +457,8 @@ { int err; + sx_assert(&ifnet_detach_sxlock, SA_XLOCKED); + /* * Given that the cloned ifnet might be attached to a different * vnet from where its cloner was registered, we have to @@ -361,7 +476,7 @@ if (err != 0) ifc_link_ifp(ifc, ifp); - else if (ifc->ifc_flags & IFC_F_AUTOUNIT) + else if ((ifc->ifc_flags & IFC_F_AUTOUNIT) && (!(flags & IFC_F_MOVED))) ifc_free_unit(ifc, unit); CURVNET_RESTORE(); return (err); @@ -419,10 +534,12 @@ return (NULL); struct if_clone *ifc = if_clone_alloc(name, req->maxunit); + ifc->ifc_match = req->match_f != NULL ? req->match_f : ifc_simple_match; ifc->ifc_create = req->create_f; ifc->ifc_destroy = req->destroy_f; - ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP)); + ifc->ifc_vmove = (req->vmove_f != NULL) ? req->vmove_f : if_vmove_simple; + ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP | IFC_F_NOMOVE)); if (if_clone_attach(ifc) != 0) return (NULL); @@ -472,6 +589,7 @@ ifc->ifc_destroy = ifc_advanced_destroy_wrapper; ifc->ifca_destroy = destroy; ifc->ifca_create = create; + ifc->ifc_flags = IFC_F_NOMOVE; if (if_clone_attach(ifc) != 0) return (NULL); @@ -518,7 +636,7 @@ ifc->ifcs_create = create; ifc->ifcs_destroy = destroy; ifc->ifcs_minifs = minifs; - ifc->ifc_flags = IFC_F_AUTOUNIT; + ifc->ifc_flags = IFC_F_AUTOUNIT | IFC_F_NOMOVE; if (if_clone_attach(ifc) != 0) return (NULL); diff --git a/sys/net/if_epair.c b/sys/net/if_epair.c --- a/sys/net/if_epair.c +++ b/sys/net/if_epair.c @@ -118,7 +118,9 @@ struct ifnet *ifp; /* This ifp. */ struct ifnet *oifp; /* other ifp of pair. */ int num_queues; + bool is_moved; /* true if moved */ struct epair_queue *queues; + struct if_clone *ifc; /* cloner used to create this instance */ struct ifmedia media; /* Media config (fake). */ STAILQ_ENTRY(epair_softc) entry; }; @@ -232,49 +234,40 @@ } static void -epair_menq(struct mbuf *m, struct epair_softc *osc) +epair_menq(struct epair_queue *q, struct mbuf *m, struct ifnet *input_ifp, + struct ifnet *output_ifp) { - struct ifnet *ifp, *oifp; int len, ret; int ridx; short mflags; - /* - * I know this looks weird. We pass the "other sc" as we need that one - * and can get both ifps from it as well. - */ - oifp = osc->ifp; - ifp = osc->oifp; - - epair_prepare_mbuf(m, oifp); + epair_prepare_mbuf(m, input_ifp); /* Save values as once the mbuf is queued, it's not ours anymore. */ len = m->m_pkthdr.len; mflags = m->m_flags; - struct epair_queue *q = epair_select_queue(osc, m); - atomic_set_long(&q->state, (1 << BIT_MBUF_QUEUED)); ridx = atomic_load_int(&q->ridx); ret = buf_ring_enqueue(q->rxring[ridx], m); if (ret != 0) { /* Ring is full. */ - if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); + if_inc_counter(output_ifp, IFCOUNTER_OQDROPS, 1); m_freem(m); return; } - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + if_inc_counter(output_ifp, IFCOUNTER_OPACKETS, 1); /* * IFQ_HANDOFF_ADJ/ip_handoff() update statistics, * but as we bypass all this we have to duplicate * the logic another time. */ - if_inc_counter(ifp, IFCOUNTER_OBYTES, len); + if_inc_counter(output_ifp, IFCOUNTER_OBYTES, len); if (mflags & (M_BCAST|M_MCAST)) - if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); + if_inc_counter(output_ifp, IFCOUNTER_OMCASTS, 1); /* Someone else received the packet. */ - if_inc_counter(oifp, IFCOUNTER_IPACKETS, 1); + if_inc_counter(input_ifp, IFCOUNTER_IPACKETS, 1); if (!atomic_testandset_long(&q->state, BIT_QUEUE_TASK)) taskqueue_enqueue(epair_tasks.tq[q->id], &q->tx_task); @@ -294,8 +287,7 @@ * other interface (oifp) of our pair. */ sc = ifp->if_softc; - oifp = sc->oifp; - sc = oifp->if_softc; + oifp = atomic_load_ptr(&sc->oifp); for (;;) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) @@ -306,13 +298,14 @@ /* In case either interface is not usable drop the packet. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (ifp->if_flags & IFF_UP) == 0 || - (oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + (oifp == NULL || oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (oifp->if_flags & IFF_UP) == 0) { m_freem(m); continue; } - epair_menq(m, sc); + struct epair_queue *q = epair_select_queue(oifp->if_softc, m); + epair_menq(q, m, oifp, ifp); } } @@ -354,8 +347,8 @@ * drop the packet. */ sc = ifp->if_softc; - oifp = sc->oifp; - if ((oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + oifp = atomic_load_ptr(&sc->oifp); + if (oifp == NULL || (oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (oifp->if_flags & IFF_UP) == 0) { if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); @@ -385,7 +378,8 @@ IF_UNLOCK(&ifp->if_snd); #endif - epair_menq(m, oifp->if_softc); + struct epair_queue *q = epair_select_queue(oifp->if_softc, m); + epair_menq(q, m, oifp, ifp); return (0); } @@ -481,18 +475,16 @@ } static void -epair_clone_add(struct if_clone *ifc, struct epair_softc *scb) +epair_clone_add(struct if_clone *ifc, const struct ifnet *src_ifp, struct ifnet *dst_ifp) { - struct ifnet *ifp; uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ - ifp = scb->ifp; /* Copy epairNa etheraddr and change the last byte. */ - memcpy(eaddr, scb->oifp->if_hw_addr, ETHER_ADDR_LEN); + memcpy(eaddr, src_ifp->if_hw_addr, ETHER_ADDR_LEN); eaddr[5] = 0x0b; - ether_ifattach(ifp, eaddr); + ether_ifattach(dst_ifp, eaddr); - if_clone_addif(ifc, ifp); + if_clone_addif(ifc, dst_ifp); } static struct epair_softc * @@ -505,6 +497,7 @@ return (NULL); sc = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO); + sc->ifc = ifc; sc->ifp = ifp; sc->num_queues = epair_tasks.tasks; sc->queues = mallocarray(sc->num_queues, sizeof(struct epair_queue), @@ -690,7 +683,6 @@ struct ifc_data *ifd, struct ifnet **ifpp) { struct epair_softc *sca, *scb; - struct ifnet *ifp; char *dp; int error, unit; uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ @@ -716,11 +708,10 @@ scb->oifp = sca->ifp; /* Finish initialization of interface a. */ - ifp = sca->ifp; epair_setup_ifp(sca, name, unit); epair_generate_mac(sca, eaddr); - ether_ifattach(ifp, eaddr); + ether_ifattach(sca->ifp, eaddr); /* Swap the name and finish initialization of interface b. */ dp = name + strlen(name) - 1; @@ -728,13 +719,12 @@ epair_setup_ifp(scb, name, unit); - ifp = scb->ifp; /* We need to play some tricks here for the second interface. */ strlcpy(name, epairname, len); /* Correctly set the name for the cloner list. */ strlcpy(name, scb->ifp->if_xname, len); - epair_clone_add(ifc, scb); + epair_clone_add(ifc, sca->ifp, scb->ifp); /* * Restore name to a as the ifp for this will go into the @@ -776,47 +766,116 @@ struct ifnet *oifp; struct epair_softc *sca, *scb; int unit, error; + bool is_moved; - /* - * In case we called into if_clone_destroyif() ourselves - * again to remove the second interface, the softc will be - * NULL. In that case so not do anything but return success. - */ - if (ifp->if_softc == NULL) - return (0); + sx_assert(&ifnet_detach_sxlock, SA_XLOCKED); unit = ifp->if_dunit; sca = ifp->if_softc; - oifp = sca->oifp; - scb = oifp->if_softc; + is_moved = sca->is_moved; + oifp = atomic_load_ptr(&sca->oifp); /* Frist get the interfaces down and detached. */ epair_set_state(ifp, false); - epair_set_state(oifp, false); - + if (oifp != NULL) + epair_set_state(oifp, false); ether_ifdetach(ifp); - ether_ifdetach(oifp); - - /* Third free any queued packets and all the resources. */ - CURVNET_SET_QUIET(oifp->if_vnet); - epair_drain_rings(scb); - oifp->if_softc = NULL; - error = if_clone_destroyif(ifc, oifp); - if (error) - panic("%s: if_clone_destroyif() for our 2nd iface failed: %d", - __func__, error); - epair_free_sc(scb); - CURVNET_RESTORE(); + + if (oifp != NULL) { + CURVNET_SET_QUIET(oifp->if_vnet); + + /* Rely on ifnet_detach_sxlock lock held */ + scb = oifp->if_softc; + scb->oifp = NULL; + sca->oifp = NULL; + + bool result = ifc_unlink_ifp(scb->ifc, oifp); + KASSERT(result == true, ("%s: unable to unlink interface %s", + __func__, if_name(oifp))); + error = epair_clone_destroy(scb->ifc, oifp, flags); + if (error) + panic("%s: if_clone_destroyif() for our 2nd iface failed: %d", + __func__, error); + CURVNET_RESTORE(); + } epair_drain_rings(sca); epair_free_sc(sca); /* Last free the cloner unit. */ - ifc_free_unit(ifc, unit); + if (!is_moved && oifp != NULL) + ifc_free_unit(ifc, unit); return (0); } +static int +epair_clone_vmove(struct if_clone *ifc, struct ifnet *ifp_src, + struct ifc_data *ifd, struct ifnet **ifpp) +{ + struct ifc_vparam_data vdata = {}; + struct ifnet *oifp; + struct epair_softc *sca_src, *sca_dst, *scb; + uint8_t eaddr[ETHER_ADDR_LEN]; + int error = 0; + + sx_assert(&ifnet_detach_sxlock, SA_XLOCKED); + + sca_src = ifp_src->if_softc; + if (sca_src == NULL) + return (EINVAL); + + /* Copy necessary data to re-create the interface */ + ifc_save_vparams(ifp_src, &vdata); + + /* Create dst interface first to perform atomic swap */ + CURVNET_SET_QUIET(ifd->vnet); + sca_dst = epair_alloc_sc(ifc); + if (sca_dst == NULL) { + ifc_apply_vparams(NULL, &vdata); + CURVNET_RESTORE(); + return (ENOSPC); + } + + if_ref(sca_dst->ifp); + + epair_setup_ifp(sca_dst, vdata.ifname, vdata.unit); + memcpy(eaddr, ifp_src->if_hw_addr, sizeof(eaddr)); + ifc_apply_vparams(sca_dst->ifp, &vdata); + ether_ifattach(sca_dst->ifp, eaddr); + + /* Perform swap */ + oifp = sca_src->oifp; + if (oifp != NULL) { + scb = oifp->if_softc; + scb->oifp = sca_dst->ifp; + sca_dst->oifp = scb->ifp; + /* Unlink old interface from the pair */ + sca_src->oifp = NULL; + } else + error = EINVAL; + + if (error == 0) { + /* + * Migration successful, destroy old interface. + * if_clone_destroyif() mananages vnet automatically. + */ + sca_src->is_moved = true; + if_clone_destroyif(ifc, ifp_src); + epair_set_state(sca_dst->ifp, true); + *ifpp = sca_dst->ifp; + } else { + /* Migration failed, remove state */ + sca_dst->is_moved = true; + if_clone_destroyif(ifc, sca_dst->ifp); + } + if_rele(sca_dst->ifp); + + CURVNET_RESTORE(); + + return (error); +} + static void vnet_epair_init(const void *unused __unused) { @@ -824,6 +883,7 @@ .match_f = epair_clone_match, .create_f = epair_clone_create, .destroy_f = epair_clone_destroy, + .vmove_f = epair_clone_vmove, }; V_epair_cloner = ifc_attach_cloner(epairname, &req); } diff --git a/sys/net/if_var.h b/sys/net/if_var.h --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -739,6 +739,7 @@ int if_setcapenablebit(if_t ifp, int setcap, int clearcap); int if_getcapenable(if_t ifp); const char *if_getdname(if_t ifp); +char *if_copydescr(if_t ifp); void if_setdescr(if_t ifp, char *descrbuf); void if_freedescr(char *descrbuf); int if_setdev(if_t ifp, void *dev); diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -326,6 +326,8 @@ static int vlan_clone_create(struct if_clone *, char *, size_t, struct ifc_data *, struct ifnet **); static int vlan_clone_destroy(struct if_clone *, struct ifnet *, uint32_t); +static int vlan_clone_vmove(struct if_clone *, struct ifnet *, + struct ifc_data *, struct ifnet **); static void vlan_ifdetach(void *arg, struct ifnet *ifp); static void vlan_iflladdr(void *arg, struct ifnet *ifp); @@ -907,6 +909,7 @@ .match_f = vlan_clone_match, .create_f = vlan_clone_create, .destroy_f = vlan_clone_destroy, + .vmove_f = vlan_clone_vmove, }; static int @@ -1061,6 +1064,17 @@ return (1); } +struct vlanparams { + struct ifnet *parent; + int vid; + uint16_t proto; + uint8_t pcp; +}; + +static int +vlan_create_specific(struct if_clone *ifc, char *name, int unit, + struct vlanparams *vp, struct ifnet **ifpp); + static int vlan_clone_create(struct if_clone *ifc, char *name, size_t len, struct ifc_data *ifd, struct ifnet **ifpp) @@ -1072,13 +1086,8 @@ int error; int vid = 0; uint16_t proto = ETHERTYPE_VLAN; - struct ifvlan *ifv; - struct ifnet *ifp; struct ifnet *p = NULL; - struct ifaddr *ifa; - struct sockaddr_dl *sdl; struct vlanreq vlr; - static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ /* @@ -1159,14 +1168,38 @@ } } + struct vlanparams vp = { + .parent = p, + .vid = vid, + .proto = proto, + }; + + error = vlan_create_specific(ifc, name, unit, &vp, ifpp); + if (error != 0) { + if (!subinterface) + ifc_free_unit(ifc, unit); + } + if (p != NULL) + if_rele(p); + + return (error); +} + +static int +vlan_create_specific(struct if_clone *ifc, char *name, int unit, + struct vlanparams *vp, struct ifnet **ifpp) +{ + struct ifvlan *ifv; + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ + int error; + ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - if (!subinterface) - ifc_free_unit(ifc, unit); free(ifv, M_VLAN); - if (p != NULL) - if_rele(p); return (ENOSPC); } CK_SLIST_INIT(&ifv->vlan_mc_listhead); @@ -1205,9 +1238,8 @@ sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_L2VLAN; - if (p != NULL) { - error = vlan_config(ifv, p, vid, proto); - if_rele(p); + if (vp->parent != NULL) { + error = vlan_config(ifv, vp->parent, vp->vid, vp->proto); if (error != 0) { /* * Since we've partially failed, we need to back @@ -1217,8 +1249,6 @@ ether_ifdetach(ifp); vlan_unconfig(ifp); if_free(ifp); - if (!subinterface) - ifc_free_unit(ifc, unit); free(ifv, M_VLAN); return (error); @@ -1258,6 +1288,45 @@ return (0); } +static int +vlan_clone_vmove(struct if_clone *ifc, struct ifnet *ifp_src, + struct ifc_data *ifd, struct ifnet **ifpp) +{ + struct ifc_vparam_data vdata = {}; + struct ifvlan *ifv; + int error; + + VLAN_XLOCK(); + + /* Copy necessary data to re-create the interface */ + ifc_save_vparams(ifp_src, &vdata); + ifv = ifp_src->if_softc; + + struct vlanparams vp = { + .parent = (ifv->ifv_trunk != NULL) ? ifv->ifv_trunk->parent : NULL, + .vid = ifv->ifv_vid, + .proto = ifv->ifv_proto, + }; + error = if_clone_destroyif(ifc, ifp_src); + VLAN_XUNLOCK(); + ifp_src = NULL; + + if (error != 0) + return (error); + + CURVNET_SET_QUIET(ifd->vnet); + + VLAN_XLOCK(); + error = vlan_create_specific(ifc, vdata.ifname, + vdata.unit, &vp, ifpp); + VLAN_XUNLOCK(); + ifc_apply_vparams(*ifpp, &vdata); + + CURVNET_RESTORE(); + + return (error); +} + /* * The ifp->if_init entry point for vlan(4) is a no-op. */ @@ -1519,7 +1588,7 @@ } static int -vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, +vlan_config_locked(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, uint16_t proto) { struct epoch_tracker et; @@ -1549,8 +1618,6 @@ if (trunk->parent != p) return (EBUSY); - VLAN_XLOCK(); - ifv->ifv_proto = proto; if (ifv->ifv_vid != vid) { @@ -1559,11 +1626,9 @@ ifv->ifv_vid = vid; error = vlan_inshash(trunk, ifv); } - /* Will unlock */ - goto done; + return (error); } - VLAN_XLOCK(); if (p->if_vlantrunk == NULL) { trunk = malloc(sizeof(struct ifvlantrunk), M_VLAN, M_WAITOK | M_ZERO); @@ -1581,8 +1646,8 @@ ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ ifv->ifv_pcp = 0; /* Default: best effort delivery. */ error = vlan_inshash(trunk, ifv); - if (error) - goto done; + if (error != 0) + return (error); ifv->ifv_proto = proto; ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; ifv->ifv_mintu = ETHERMIN; @@ -1677,7 +1742,15 @@ */ (void)vlan_setmulti(ifp); -done: + return (error); +} + +static int +vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid, + uint16_t proto) +{ + VLAN_XLOCK(); + int error = vlan_config_locked(ifv, p, vid, proto); if (error == 0) EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid); VLAN_XUNLOCK();