Changeset View
Changeset View
Standalone View
Standalone View
head/sys/net/if.c
Show First 20 Lines • Show All 269 Lines • ▼ Show 20 Lines | |||||
static int if_delmulti_locked(struct ifnet *, struct ifmultiaddr *, int); | static int if_delmulti_locked(struct ifnet *, struct ifmultiaddr *, int); | ||||
static void do_link_state_change(void *, int); | static void do_link_state_change(void *, int); | ||||
static int if_getgroup(struct ifgroupreq *, struct ifnet *); | static int if_getgroup(struct ifgroupreq *, struct ifnet *); | ||||
static int if_getgroupmembers(struct ifgroupreq *); | static int if_getgroupmembers(struct ifgroupreq *); | ||||
static void if_delgroups(struct ifnet *); | static void if_delgroups(struct ifnet *); | ||||
static void if_attach_internal(struct ifnet *, int, struct if_clone *); | static void if_attach_internal(struct ifnet *, int, struct if_clone *); | ||||
static int if_detach_internal(struct ifnet *, int, struct if_clone **); | static int if_detach_internal(struct ifnet *, int, struct if_clone **); | ||||
static void if_siocaddmulti(void *, int); | static void if_siocaddmulti(void *, int); | ||||
static void if_link_ifnet(struct ifnet *); | |||||
static bool if_unlink_ifnet(struct ifnet *, bool); | |||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
static int if_vmove(struct ifnet *, struct vnet *); | static int if_vmove(struct ifnet *, struct vnet *); | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
/* | /* | ||||
* XXX: declare here to avoid to include many inet6 related files.. | * XXX: declare here to avoid to include many inet6 related files.. | ||||
* should be more generalized? | * should be more generalized? | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | VNET_ASSERT(CK_STAILQ_EMPTY(&V_ifg_head), ("%s:%d tailq &V_ifg_head=%p " | ||||
"not empty", __func__, __LINE__, &V_ifg_head)); | "not empty", __func__, __LINE__, &V_ifg_head)); | ||||
free((caddr_t)V_ifindex_table, M_IFNET); | free((caddr_t)V_ifindex_table, M_IFNET); | ||||
} | } | ||||
VNET_SYSUNINIT(vnet_if_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST, | VNET_SYSUNINIT(vnet_if_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST, | ||||
vnet_if_uninit, NULL); | vnet_if_uninit, NULL); | ||||
static void | static void | ||||
if_link_ifnet(struct ifnet *ifp) | |||||
{ | |||||
IFNET_WLOCK(); | |||||
CK_STAILQ_INSERT_TAIL(&V_ifnet, ifp, if_link); | |||||
#ifdef VIMAGE | |||||
curvnet->vnet_ifcnt++; | |||||
#endif | |||||
IFNET_WUNLOCK(); | |||||
} | |||||
static bool | |||||
if_unlink_ifnet(struct ifnet *ifp, bool vmove) | |||||
{ | |||||
struct ifnet *iter; | |||||
int found = 0; | |||||
IFNET_WLOCK(); | |||||
CK_STAILQ_FOREACH(iter, &V_ifnet, if_link) | |||||
if (iter == ifp) { | |||||
CK_STAILQ_REMOVE(&V_ifnet, ifp, ifnet, if_link); | |||||
if (!vmove) | |||||
ifp->if_flags |= IFF_DYING; | |||||
found = 1; | |||||
break; | |||||
} | |||||
#ifdef VIMAGE | |||||
curvnet->vnet_ifcnt--; | |||||
#endif | |||||
IFNET_WUNLOCK(); | |||||
return (found); | |||||
} | |||||
static void | |||||
vnet_if_return(const void *unused __unused) | vnet_if_return(const void *unused __unused) | ||||
{ | { | ||||
struct ifnet *ifp, *nifp; | struct ifnet *ifp, *nifp; | ||||
struct ifnet **pending; | |||||
int found, i; | |||||
i = 0; | |||||
/* | |||||
* We need to protect our access to the V_ifnet tailq. Ordinarily we'd | |||||
* enter NET_EPOCH, but that's not possible, because if_vmove() calls | |||||
* if_detach_internal(), which waits for NET_EPOCH callbacks to | |||||
* complete. We can't do that from within NET_EPOCH. | |||||
* | |||||
* However, we can also use the IFNET_xLOCK, which is the V_ifnet | |||||
* read/write lock. We cannot hold the lock as we call if_vmove() | |||||
* though, as that presents LOR w.r.t ifnet_sx, in_multi_sx and iflib | |||||
* ctx lock. | |||||
*/ | |||||
IFNET_WLOCK(); | |||||
pending = malloc(sizeof(struct ifnet *) * curvnet->vnet_ifcnt, | |||||
M_IFNET, M_WAITOK | M_ZERO); | |||||
/* Return all inherited interfaces to their parent vnets. */ | /* Return all inherited interfaces to their parent vnets. */ | ||||
CK_STAILQ_FOREACH_SAFE(ifp, &V_ifnet, if_link, nifp) { | CK_STAILQ_FOREACH_SAFE(ifp, &V_ifnet, if_link, nifp) { | ||||
if (ifp->if_home_vnet != ifp->if_vnet) | if (ifp->if_home_vnet != ifp->if_vnet) { | ||||
if_vmove(ifp, ifp->if_home_vnet); | found = if_unlink_ifnet(ifp, true); | ||||
MPASS(found); | |||||
pending[i++] = ifp; | |||||
} | } | ||||
} | } | ||||
IFNET_WUNLOCK(); | |||||
for (int j = 0; j < i; j++) { | |||||
if_vmove(pending[j], pending[j]->if_home_vnet); | |||||
} | |||||
free(pending, M_IFNET); | |||||
} | |||||
VNET_SYSUNINIT(vnet_if_return, SI_SUB_VNET_DONE, SI_ORDER_ANY, | VNET_SYSUNINIT(vnet_if_return, SI_SUB_VNET_DONE, SI_ORDER_ANY, | ||||
vnet_if_return, NULL); | vnet_if_return, NULL); | ||||
#endif | #endif | ||||
static void * | static void * | ||||
if_grow(void) | if_grow(void) | ||||
{ | { | ||||
int oldlim; | int oldlim; | ||||
▲ Show 20 Lines • Show All 412 Lines • ▼ Show 20 Lines | for (ifa = ifp->if_addr; ifa != NULL; | ||||
if (ifa->ifa_addr->sa_family == AF_LINK) { | if (ifa->ifa_addr->sa_family == AF_LINK) { | ||||
sdl = (struct sockaddr_dl *)ifa->ifa_addr; | sdl = (struct sockaddr_dl *)ifa->ifa_addr; | ||||
sdl->sdl_index = ifp->if_index; | sdl->sdl_index = ifp->if_index; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
IFNET_WLOCK(); | if_link_ifnet(ifp); | ||||
CK_STAILQ_INSERT_TAIL(&V_ifnet, ifp, if_link); | |||||
#ifdef VIMAGE | |||||
curvnet->vnet_ifcnt++; | |||||
#endif | |||||
IFNET_WUNLOCK(); | |||||
if (domain_init_status >= 2) | if (domain_init_status >= 2) | ||||
if_attachdomain1(ifp); | if_attachdomain1(ifp); | ||||
EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); | EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); | ||||
if (IS_DEFAULT_VNET(curvnet)) | if (IS_DEFAULT_VNET(curvnet)) | ||||
devctl_notify("IFNET", ifp->if_xname, "ATTACH", NULL); | devctl_notify("IFNET", ifp->if_xname, "ATTACH", NULL); | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | |||||
* one vnet to another, where it must be fully operational. | * one vnet to another, where it must be fully operational. | ||||
* | * | ||||
* XXXRW: There are some significant questions about event ordering, and | * XXXRW: There are some significant questions about event ordering, and | ||||
* how to prevent things from starting to use the interface during detach. | * how to prevent things from starting to use the interface during detach. | ||||
*/ | */ | ||||
void | void | ||||
if_detach(struct ifnet *ifp) | if_detach(struct ifnet *ifp) | ||||
{ | { | ||||
bool found; | |||||
CURVNET_SET_QUIET(ifp->if_vnet); | CURVNET_SET_QUIET(ifp->if_vnet); | ||||
found = if_unlink_ifnet(ifp, false); | |||||
if (found) | |||||
if_detach_internal(ifp, 0, NULL); | if_detach_internal(ifp, 0, NULL); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
/* | /* | ||||
* The vmove flag, if set, indicates that we are called from a callpath | * The vmove flag, if set, indicates that we are called from a callpath | ||||
* that is moving an interface to a different vnet instance. | * that is moving an interface to a different vnet instance. | ||||
* | * | ||||
* The shutdown flag, if set, indicates that we are called in the | * The shutdown flag, if set, indicates that we are called in the | ||||
* process of shutting down a vnet instance. Currently only the | * process of shutting down a vnet instance. Currently only the | ||||
* vnet_if_return SYSUNINIT function sets it. Note: we can be called | * vnet_if_return SYSUNINIT function sets it. Note: we can be called | ||||
* on a vnet instance shutdown without this flag being set, e.g., when | * on a vnet instance shutdown without this flag being set, e.g., when | ||||
* the cloned interfaces are destoyed as first thing of teardown. | * the cloned interfaces are destoyed as first thing of teardown. | ||||
*/ | */ | ||||
static int | static int | ||||
if_detach_internal(struct ifnet *ifp, int vmove, struct if_clone **ifcp) | if_detach_internal(struct ifnet *ifp, int vmove, struct if_clone **ifcp) | ||||
{ | { | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
int i; | int i; | ||||
struct domain *dp; | struct domain *dp; | ||||
struct ifnet *iter; | |||||
int found = 0; | |||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
bool shutdown; | bool shutdown; | ||||
shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); | shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); | ||||
#endif | #endif | ||||
IFNET_WLOCK(); | |||||
CK_STAILQ_FOREACH(iter, &V_ifnet, if_link) | |||||
if (iter == ifp) { | |||||
CK_STAILQ_REMOVE(&V_ifnet, ifp, ifnet, if_link); | |||||
if (!vmove) | |||||
ifp->if_flags |= IFF_DYING; | |||||
found = 1; | |||||
break; | |||||
} | |||||
IFNET_WUNLOCK(); | |||||
if (!found) { | |||||
/* | |||||
* While we would want to panic here, we cannot | |||||
* guarantee that the interface is indeed still on | |||||
* the list given we don't hold locks all the way. | |||||
*/ | |||||
return (ENOENT); | |||||
#if 0 | |||||
if (vmove) | |||||
panic("%s: ifp=%p not on the ifnet tailq %p", | |||||
__func__, ifp, &V_ifnet); | |||||
else | |||||
return; /* XXX this should panic as well? */ | |||||
#endif | |||||
} | |||||
/* | /* | ||||
* At this point we know the interface still was on the ifnet list | * At this point we know the interface still was on the ifnet list | ||||
* and we removed it so we are in a stable state. | * and we removed it so we are in a stable state. | ||||
*/ | */ | ||||
#ifdef VIMAGE | |||||
curvnet->vnet_ifcnt--; | |||||
#endif | |||||
epoch_wait_preempt(net_epoch_preempt); | epoch_wait_preempt(net_epoch_preempt); | ||||
/* | /* | ||||
* Ensure all pending EPOCH(9) callbacks have been executed. This | * Ensure all pending EPOCH(9) callbacks have been executed. This | ||||
* fixes issues about late destruction of multicast options | * fixes issues about late destruction of multicast options | ||||
* which lead to leave group calls, which in turn access the | * which lead to leave group calls, which in turn access the | ||||
* belonging ifnet structure: | * belonging ifnet structure: | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | |||||
* Move an ifnet to or from another child prison/vnet, specified by the jail id. | * Move an ifnet to or from another child prison/vnet, specified by the jail id. | ||||
*/ | */ | ||||
static int | static int | ||||
if_vmove_loan(struct thread *td, struct ifnet *ifp, char *ifname, int jid) | if_vmove_loan(struct thread *td, struct ifnet *ifp, char *ifname, int jid) | ||||
{ | { | ||||
struct prison *pr; | struct prison *pr; | ||||
struct ifnet *difp; | struct ifnet *difp; | ||||
int error; | int error; | ||||
bool found; | |||||
bool shutdown; | bool shutdown; | ||||
/* Try to find the prison within our visibility. */ | /* Try to find the prison within our visibility. */ | ||||
sx_slock(&allprison_lock); | sx_slock(&allprison_lock); | ||||
pr = prison_find_child(td->td_ucred->cr_prison, jid); | pr = prison_find_child(td->td_ucred->cr_prison, jid); | ||||
sx_sunlock(&allprison_lock); | sx_sunlock(&allprison_lock); | ||||
if (pr == NULL) | if (pr == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 20 Lines | if_vmove_loan(struct thread *td, struct ifnet *ifp, char *ifname, int jid) | ||||
shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); | shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); | ||||
if (shutdown) { | if (shutdown) { | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
prison_free(pr); | prison_free(pr); | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
found = if_unlink_ifnet(ifp, true); | |||||
MPASS(found); | |||||
/* Move the interface into the child jail/vnet. */ | /* Move the interface into the child jail/vnet. */ | ||||
error = if_vmove(ifp, pr->pr_vnet); | error = if_vmove(ifp, pr->pr_vnet); | ||||
/* Report the new if_xname back to the userland on success. */ | /* Report the new if_xname back to the userland on success. */ | ||||
if (error == 0) | if (error == 0) | ||||
sprintf(ifname, "%s", ifp->if_xname); | sprintf(ifname, "%s", ifp->if_xname); | ||||
prison_free(pr); | prison_free(pr); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
if_vmove_reclaim(struct thread *td, char *ifname, int jid) | if_vmove_reclaim(struct thread *td, char *ifname, int jid) | ||||
{ | { | ||||
struct prison *pr; | struct prison *pr; | ||||
struct vnet *vnet_dst; | struct vnet *vnet_dst; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error; | int error, found; | ||||
bool shutdown; | bool shutdown; | ||||
/* Try to find the prison within our visibility. */ | /* Try to find the prison within our visibility. */ | ||||
sx_slock(&allprison_lock); | sx_slock(&allprison_lock); | ||||
pr = prison_find_child(td->td_ucred->cr_prison, jid); | pr = prison_find_child(td->td_ucred->cr_prison, jid); | ||||
sx_sunlock(&allprison_lock); | sx_sunlock(&allprison_lock); | ||||
if (pr == NULL) | if (pr == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 21 Lines | if_vmove_reclaim(struct thread *td, char *ifname, int jid) | ||||
shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); | shutdown = VNET_IS_SHUTTING_DOWN(ifp->if_vnet); | ||||
if (shutdown) { | if (shutdown) { | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
prison_free(pr); | prison_free(pr); | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
/* Get interface back from child jail/vnet. */ | /* Get interface back from child jail/vnet. */ | ||||
found = if_unlink_ifnet(ifp, true); | |||||
MPASS(found); | |||||
error = if_vmove(ifp, vnet_dst); | error = if_vmove(ifp, vnet_dst); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
/* Report the new if_xname back to the userland on success. */ | /* Report the new if_xname back to the userland on success. */ | ||||
if (error == 0) | if (error == 0) | ||||
sprintf(ifname, "%s", ifp->if_xname); | sprintf(ifname, "%s", ifp->if_xname); | ||||
prison_free(pr); | prison_free(pr); | ||||
▲ Show 20 Lines • Show All 3,069 Lines • Show Last 20 Lines |