diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -708,7 +708,6 @@ bool if_try_ref(struct ifnet *ifp) { - NET_EPOCH_ASSERT(); return (refcount_acquire_if_not_zero(&ifp->if_refcount)); } @@ -1360,6 +1359,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 +1431,13 @@ return (EBUSY); } + error = ifc_vmove(ifp, vnet_dst, NULL); + 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); 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 @@ -56,6 +56,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_src, struct ifnet *ifp_src, + struct ifc_data *ifd, struct if_clone *ifc_dst, struct ifnet **ifpp); #define IFC_D_FORCE 0x10 struct if_clone_addreq { @@ -66,15 +68,18 @@ ifc_match_f *match_f; ifc_create_f *create_f; ifc_destroy_f *destroy_f; + ifc_vmove_f *vmove_f; }; #define IFC_C_NOGROUP 0x01 #define IFC_C_AUTOUNIT 0x02 #define IFC_NOGROUP IFC_C_NOGROUP +#define IFC_C_NOMOVE 0x08 struct if_clone *ifc_attach_cloner(const char *name, struct if_clone_addreq *req); void ifc_detach_cloner(struct if_clone *ifc); int ifc_create_ifp(const char *name, struct ifc_data *ifd, struct ifnet **ifpp); +int ifc_vmove(struct ifnet *ifp, struct vnet *vnet, struct ifnet **ifpp); int ifc_copyin(const struct ifc_data *ifd, void *target, size_t len); #ifdef CLONE_COMPAT_13 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 @@ -75,6 +75,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. */ @@ -239,6 +242,93 @@ IF_CLONE_UNLOCK(ifc); } +#ifdef VIMAGE +/* + * "Default" vmove handler deleting and re-creating interface + * without any creation params + */ +static int +if_vmove_simple(struct if_clone *ifc_src, struct ifnet *ifp_src, struct ifc_data *ifd, + struct if_clone *ifc_dst, struct ifnet **ifpp) +{ + char ifname[IFNAMSIZ]; + int error; + + strlcpy(ifname, if_name(ifp_src), sizeof(ifname)); + error = if_clone_destroyif(ifc_src, ifp_src); + + if (error != 0) + return (error); + + CURVNET_SET_QUIET(ifd->vnet); + error = if_clone_createif(ifc_dst, ifname, sizeof(ifname), ifd, ifpp); + CURVNET_RESTORE(); + + return (error); +} + +static struct if_clone * +ifc_find_cloner(const char *name, struct vnet *vnet) +{ + struct if_clone *ifc; + + CURVNET_SET_QUIET(vnet); + IF_CLONERS_LOCK(); + LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { + if (strcmp(ifc->ifc_name, name) == 0) { + break; + } + } + IF_CLONERS_UNLOCK(); + CURVNET_RESTORE(); + + return (ifc); +} + +int +ifc_vmove(struct ifnet *ifp, struct vnet *vnet, + struct ifnet **ifpp) +{ + struct ifnet *ifp_dst = NULL; + int error = 0; + + if (!if_try_ref(ifp)) + return (EINVAL); + + struct if_clone *ifc_src = ifc_find_cloner(ifp->if_dname, ifp->if_vnet); + + CURVNET_SET_QUIET(vnet); + bool found = ifunit(if_name(ifp)) != NULL; + struct if_clone *ifc_dst = ifc_find_cloner(ifp->if_dname, vnet); + printf("ifc_src=%p ifc_dst=%p\n", ifc_src, ifc_dst); + + if (found) + error = EEXIST; + if (ifc_src == NULL || (ifc_src->ifc_flags & IFC_C_NOMOVE) || + ifc_dst == NULL || (ifc_dst->ifc_flags & IFC_C_NOMOVE)) + error = ENOTSUP; + + if (error == 0) { + struct ifc_data ifd = { .vnet = vnet }; + CURVNET_SET_QUIET(ifp->if_home_vnet); + error = ifc_src->ifc_vmove(ifc_src, ifp, &ifd, ifc_dst, &ifp_dst); + CURVNET_RESTORE(); + } + if_rele(ifp); + + if (error == 0) + if_clone_addif(ifc_dst, ifp_dst); + CURVNET_RESTORE(); + + printf("ifc_vmove: %p -> %p\n", ifp, ifp_dst); + + if (ifpp != NULL) + *ifpp = ifp_dst; + + return (error); +} +#endif + /* * Create a clone network interface. */ @@ -282,16 +372,7 @@ if (ifp == NULL) return (ENXIO); - /* Find the cloner for this interface */ - CURVNET_SET_QUIET(ifp->if_home_vnet); - IF_CLONERS_LOCK(); - LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { - if (strcmp(ifc->ifc_name, ifp->if_dname) == 0) { - break; - } - } - IF_CLONERS_UNLOCK(); - CURVNET_RESTORE(); + ifc = ifc_find_cloner(ifp->if_dname, ifp->if_home_vnet); if (ifc == NULL) { if_rele(ifp); return (EINVAL); @@ -328,6 +409,7 @@ IF_CLONE_UNLOCK(ifc); if (ifcifp == NULL) { CURVNET_RESTORE(); + printf("Iface %s not found in target vnet llist\n", if_name(ifp)); return (ENXIO); /* ifp is not on the list. */ } if ((ifc->ifc_flags & IFC_C_NOGROUP) == 0) @@ -401,10 +483,11 @@ 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_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_C_AUTOUNIT | IFC_C_NOGROUP)); + ifc->ifc_vmove = (req->vmove_f != NULL) ? req->vmove_f : if_vmove_simple; + ifc->ifc_flags = (req->flags & (IFC_C_AUTOUNIT | IFC_C_NOGROUP | IFC_C_NOMOVE)); if (if_clone_attach(ifc) != 0) return (NULL); @@ -454,6 +537,7 @@ ifc->ifc_destroy = ifc_advanced_destroy_wrapper; ifc->ifca_destroy = destroy; ifc->ifca_create = create; + ifc->ifc_flags = IFC_C_NOMOVE; if (if_clone_attach(ifc) != 0) return (NULL); @@ -500,7 +584,7 @@ ifc->ifcs_create = create; ifc->ifcs_destroy = destroy; ifc->ifcs_minifs = minifs; - ifc->ifc_flags = IFC_C_AUTOUNIT; + ifc->ifc_flags = IFC_C_AUTOUNIT | IFC_C_NOMOVE; if (if_clone_attach(ifc) != 0) return (NULL); 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 if_clone *, struct ifnet **); static void vlan_ifdetach(void *arg, struct ifnet *ifp); static void vlan_iflladdr(void *arg, struct ifnet *ifp); @@ -903,6 +905,13 @@ /* For if_link_state_change() eyes only... */ extern void (*vlan_link_state_p)(struct ifnet *); +static struct if_clone_addreq clone_req = { + .match_f = vlan_clone_match, + .create_f = vlan_clone_create, + .destroy_f = vlan_clone_destroy, + .vmove_f = vlan_clone_vmove, +}; + static int vlan_modevent(module_t mod, int type, void *data) { @@ -932,12 +941,7 @@ vlan_pcp_p = vlan_pcp; vlan_devat_p = vlan_devat; #ifndef VIMAGE - struct if_clone_addreq req = { - .match_f = vlan_clone_match, - .create_f = vlan_clone_create, - .destroy_f = vlan_clone_destroy, - }; - vlan_cloner = ifc_attach_cloner(vlanname, &req); + vlan_cloner = ifc_attach_cloner(vlanname, &clone_req); #endif if (bootverbose) printf("vlan: initialized, using " @@ -987,12 +991,7 @@ static void vnet_vlan_init(const void *unused __unused) { - struct if_clone_addreq req = { - .match_f = vlan_clone_match, - .create_f = vlan_clone_create, - .destroy_f = vlan_clone_destroy, - }; - vlan_cloner = ifc_attach_cloner(vlanname, &req); + vlan_cloner = ifc_attach_cloner(vlanname, &clone_req); V_vlan_cloner = vlan_cloner; } VNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, @@ -1065,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) @@ -1076,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 */ /* @@ -1163,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); @@ -1209,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 @@ -1221,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); @@ -1262,6 +1288,52 @@ return (0); } +static int +vlan_clone_vmove(struct if_clone *ifc_src, struct ifnet *ifp_src, + struct ifc_data *ifd, struct if_clone *ifc_dst, struct ifnet **ifpp) +{ + char ifname[IFNAMSIZ]; + struct ifvlan *ifv; + int error, unit; + + printf("vlanHERE\n"); + VLAN_XLOCK(); + + /* Copy necessary data to re-create the interface */ + 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, + }; + strlcpy(ifname, if_name(ifp_src), sizeof(ifname)); + unit = ifp_src->if_dunit; + CURVNET_SET_QUIET(ifp_src->if_vnet); + error = if_clone_destroyif(ifc_src, ifp_src); + CURVNET_RESTORE(); + printf("DESTROYING %s=%d\n", ifname, error); + VLAN_XUNLOCK(); + ifp_src = NULL; + + if (error == 0) { + CURVNET_SET_QUIET(ifd->vnet); + if (unit != IF_DUNIT_NONE) + error = ifc_alloc_unit(ifc_dst, &unit); + if (error == 0) { + VLAN_XLOCK(); + error = vlan_create_specific(ifc_dst, ifname, unit, &vp, ifpp); + printf("CREATING %s = %d\n", ifname, error); + VLAN_XUNLOCK(); + } + CURVNET_RESTORE(); + } + + printf("= cb end %d\n", error); + + return (error); +} + /* * The ifp->if_init entry point for vlan(4) is a no-op. */ @@ -1523,7 +1595,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; @@ -1553,8 +1625,6 @@ if (trunk->parent != p) return (EBUSY); - VLAN_XLOCK(); - ifv->ifv_proto = proto; if (ifv->ifv_vid != vid) { @@ -1563,11 +1633,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); @@ -1585,8 +1653,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; @@ -1681,7 +1749,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();