in bsd/net/if.c [3257:4083]
static __attribute__((noinline)) int
ifioctl_ifreq(struct socket *so, u_long cmd, struct ifreq *ifr, struct proc *p)
{
struct ifnet *ifp;
u_long ocmd = cmd;
int error = 0;
struct kev_msg ev_msg;
struct net_event_data ev_data;
bzero(&ev_data, sizeof(struct net_event_data));
bzero(&ev_msg, sizeof(struct kev_msg));
switch (cmd) {
case SIOCIFCREATE:
case SIOCIFCREATE2:
error = proc_suser(p);
if (error) {
return error;
}
return if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name),
cmd == SIOCIFCREATE2 ? ifr->ifr_data : NULL);
case SIOCIFDESTROY:
error = proc_suser(p);
if (error) {
return error;
}
return if_clone_destroy(ifr->ifr_name);
}
/*
* ioctls which require ifp. Note that we acquire dlil_ifnet_lock
* here to ensure that the ifnet, if found, has been fully attached.
*/
dlil_if_lock();
ifp = ifunit(ifr->ifr_name);
dlil_if_unlock();
if (ifp == NULL) {
return ENXIO;
}
switch (cmd) {
case SIOCGIFFLAGS:
ifnet_lock_shared(ifp);
ifr->ifr_flags = ifp->if_flags;
ifnet_lock_done(ifp);
break;
case SIOCGIFEFLAGS:
ifnet_lock_shared(ifp);
ifr->ifr_eflags = ifp->if_eflags;
ifnet_lock_done(ifp);
break;
case SIOCGIFXFLAGS:
ifnet_lock_shared(ifp);
ifr->ifr_xflags = ifp->if_xflags;
ifnet_lock_done(ifp);
break;
case SIOCGIFCAP:
ifnet_lock_shared(ifp);
ifr->ifr_reqcap = ifp->if_capabilities;
ifr->ifr_curcap = ifp->if_capenable;
ifnet_lock_done(ifp);
break;
case SIOCGIFMETRIC:
ifnet_lock_shared(ifp);
ifr->ifr_metric = ifp->if_metric;
ifnet_lock_done(ifp);
break;
case SIOCGIFMTU:
ifnet_lock_shared(ifp);
ifr->ifr_mtu = ifp->if_mtu;
ifnet_lock_done(ifp);
break;
case SIOCGIFPHYS:
ifnet_lock_shared(ifp);
ifr->ifr_phys = ifp->if_physical;
ifnet_lock_done(ifp);
break;
case SIOCSIFFLAGS:
error = proc_suser(p);
if (error != 0) {
break;
}
(void) ifnet_set_flags(ifp, ifr->ifr_flags,
(u_int16_t)~IFF_CANTCHANGE);
/*
* Note that we intentionally ignore any error from below
* for the SIOCSIFFLAGS case.
*/
(void) ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
/*
* Send the event even upon error from the driver because
* we changed the flags.
*/
dlil_post_sifflags_msg(ifp);
ifnet_touch_lastchange(ifp);
break;
case SIOCSIFCAP:
error = proc_suser(p);
if (error != 0) {
break;
}
if ((ifr->ifr_reqcap & ~ifp->if_capabilities)) {
error = EINVAL;
break;
}
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
ifnet_touch_lastchange(ifp);
break;
case SIOCSIFMETRIC:
error = proc_suser(p);
if (error != 0) {
break;
}
ifp->if_metric = ifr->ifr_metric;
ev_msg.vendor_code = KEV_VENDOR_APPLE;
ev_msg.kev_class = KEV_NETWORK_CLASS;
ev_msg.kev_subclass = KEV_DL_SUBCLASS;
ev_msg.event_code = KEV_DL_SIFMETRICS;
strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ);
ev_data.if_family = ifp->if_family;
ev_data.if_unit = (u_int32_t) ifp->if_unit;
ev_msg.dv[0].data_length = sizeof(struct net_event_data);
ev_msg.dv[0].data_ptr = &ev_data;
ev_msg.dv[1].data_length = 0;
dlil_post_complete_msg(ifp, &ev_msg);
ifnet_touch_lastchange(ifp);
break;
case SIOCSIFPHYS:
error = proc_suser(p);
if (error != 0) {
break;
}
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
if (error != 0) {
break;
}
ev_msg.vendor_code = KEV_VENDOR_APPLE;
ev_msg.kev_class = KEV_NETWORK_CLASS;
ev_msg.kev_subclass = KEV_DL_SUBCLASS;
ev_msg.event_code = KEV_DL_SIFPHYS;
strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ);
ev_data.if_family = ifp->if_family;
ev_data.if_unit = (u_int32_t) ifp->if_unit;
ev_msg.dv[0].data_length = sizeof(struct net_event_data);
ev_msg.dv[0].data_ptr = &ev_data;
ev_msg.dv[1].data_length = 0;
dlil_post_complete_msg(ifp, &ev_msg);
ifnet_touch_lastchange(ifp);
break;
case SIOCSIFMTU: {
u_int32_t oldmtu = ifp->if_mtu;
struct ifclassq *ifq = &ifp->if_snd;
error = proc_suser(p);
if (error != 0) {
break;
}
if (ifp->if_ioctl == NULL) {
error = EOPNOTSUPP;
break;
}
if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) {
error = EINVAL;
break;
}
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
if (error != 0) {
break;
}
ev_msg.vendor_code = KEV_VENDOR_APPLE;
ev_msg.kev_class = KEV_NETWORK_CLASS;
ev_msg.kev_subclass = KEV_DL_SUBCLASS;
ev_msg.event_code = KEV_DL_SIFMTU;
strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ);
ev_data.if_family = ifp->if_family;
ev_data.if_unit = (u_int32_t) ifp->if_unit;
ev_msg.dv[0].data_length = sizeof(struct net_event_data);
ev_msg.dv[0].data_ptr = &ev_data;
ev_msg.dv[1].data_length = 0;
dlil_post_complete_msg(ifp, &ev_msg);
ifnet_touch_lastchange(ifp);
rt_ifmsg(ifp);
/*
* If the link MTU changed, do network layer specific procedure
* and update all route entries associated with the interface,
* so that their MTU metric gets updated.
*/
if (ifp->if_mtu != oldmtu) {
if_rtmtu_update(ifp);
nd6_setmtu(ifp);
/* Inform all transmit queues about the new MTU */
IFCQ_LOCK(ifq);
ifnet_update_sndq(ifq, CLASSQ_EV_LINK_MTU);
IFCQ_UNLOCK(ifq);
}
break;
}
case SIOCADDMULTI:
case SIOCDELMULTI:
error = proc_suser(p);
if (error != 0) {
break;
}
/* Don't allow group membership on non-multicast interfaces. */
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
error = EOPNOTSUPP;
break;
}
/* Don't let users screw up protocols' entries. */
if (ifr->ifr_addr.sa_family != AF_UNSPEC &&
ifr->ifr_addr.sa_family != AF_LINK) {
error = EINVAL;
break;
}
if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)) {
ifr->ifr_addr.sa_len = sizeof(struct sockaddr);
}
/*
* User is permitted to anonymously join a particular link
* multicast group via SIOCADDMULTI. Subsequent join requested
* for the same record which has an outstanding refcnt from a
* past if_addmulti_anon() will not result in EADDRINUSE error
* (unlike other BSDs.) Anonymously leaving a group is also
* allowed only as long as there is an outstanding refcnt held
* by a previous anonymous request, or else ENOENT (even if the
* link-layer multicast membership exists for a network-layer
* membership.)
*/
if (cmd == SIOCADDMULTI) {
error = if_addmulti_anon(ifp, &ifr->ifr_addr, NULL);
ev_msg.event_code = KEV_DL_ADDMULTI;
} else {
error = if_delmulti_anon(ifp, &ifr->ifr_addr);
ev_msg.event_code = KEV_DL_DELMULTI;
}
if (error != 0) {
break;
}
ev_msg.vendor_code = KEV_VENDOR_APPLE;
ev_msg.kev_class = KEV_NETWORK_CLASS;
ev_msg.kev_subclass = KEV_DL_SUBCLASS;
strlcpy(&ev_data.if_name[0], ifp->if_name, IFNAMSIZ);
ev_data.if_family = ifp->if_family;
ev_data.if_unit = (u_int32_t) ifp->if_unit;
ev_msg.dv[0].data_length = sizeof(struct net_event_data);
ev_msg.dv[0].data_ptr = &ev_data;
ev_msg.dv[1].data_length = 0;
dlil_post_complete_msg(ifp, &ev_msg);
ifnet_touch_lastchange(ifp);
break;
case SIOCSIFMEDIA:
error = proc_suser(p);
if (error != 0) {
break;
}
/*
* Silently ignore setting IFM_OTHER
*/
if (ifr->ifr_media == IFM_OTHER) {
os_log_info(OS_LOG_DEFAULT,
"%s: %s SIOCSIFMEDIA ignore IFM_OTHER",
__func__, ifp->if_xname);
error = 0;
break;
}
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
if (error != 0) {
break;
}
ifnet_touch_lastchange(ifp);
break;
case SIOCDIFPHYADDR:
case SIOCSIFGENERIC:
case SIOCSIFLLADDR:
case SIOCSIFALTMTU:
case SIOCSIFVLAN:
case SIOCSIFBOND:
case SIOCSIF6LOWPAN:
error = proc_suser(p);
if (error != 0) {
break;
}
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
if (error != 0) {
break;
}
ifnet_touch_lastchange(ifp);
break;
case SIOCGIFLLADDR: {
struct sockaddr_dl *sdl = SDL(ifp->if_lladdr->ifa_addr);
if (sdl->sdl_alen == 0) {
error = EADDRNOTAVAIL;
break;
}
/* If larger than 14-bytes we'll need another mechanism */
if (sdl->sdl_alen > sizeof(ifr->ifr_addr.sa_data)) {
error = EMSGSIZE;
break;
}
/* Follow the same convention used by SIOCSIFLLADDR */
bzero(&ifr->ifr_addr, sizeof(ifr->ifr_addr));
ifr->ifr_addr.sa_family = AF_LINK;
ifr->ifr_addr.sa_len = sdl->sdl_alen;
error = ifnet_guarded_lladdr_copy_bytes(ifp,
&ifr->ifr_addr.sa_data, sdl->sdl_alen);
break;
}
case SIOCGIFTYPE:
ifr->ifr_type.ift_type = ifp->if_type;
ifr->ifr_type.ift_family = ifp->if_family;
ifr->ifr_type.ift_subfamily = ifp->if_subfamily;
break;
case SIOCGIFFUNCTIONALTYPE:
ifr->ifr_functional_type = if_functional_type(ifp, FALSE);
break;
case SIOCGIFPSRCADDR:
case SIOCGIFPDSTADDR:
case SIOCGIFGENERIC:
case SIOCGIFDEVMTU:
case SIOCGIFVLAN:
case SIOCGIFBOND:
case SIOCGIF6LOWPAN:
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
break;
case SIOCGIFWAKEFLAGS:
ifnet_lock_shared(ifp);
ifr->ifr_wake_flags = ifnet_get_wake_flags(ifp);
ifnet_lock_done(ifp);
break;
case SIOCGIFGETRTREFCNT:
ifnet_lock_shared(ifp);
ifr->ifr_route_refcnt = ifp->if_route_refcnt;
ifnet_lock_done(ifp);
break;
case SIOCSIFOPPORTUNISTIC:
case SIOCGIFOPPORTUNISTIC:
error = ifnet_getset_opportunistic(ifp, cmd, ifr, p);
break;
case SIOCGIFLINKQUALITYMETRIC:
ifnet_lock_shared(ifp);
if ((ifp->if_interface_state.valid_bitmask &
IF_INTERFACE_STATE_LQM_STATE_VALID)) {
ifr->ifr_link_quality_metric =
ifp->if_interface_state.lqm_state;
} else if (IF_FULLY_ATTACHED(ifp)) {
ifr->ifr_link_quality_metric =
IFNET_LQM_THRESH_UNKNOWN;
} else {
ifr->ifr_link_quality_metric =
IFNET_LQM_THRESH_OFF;
}
ifnet_lock_done(ifp);
break;
case SIOCSIFLOG:
case SIOCGIFLOG:
error = ifnet_getset_log(ifp, cmd, ifr, p);
break;
case SIOCGIFDELEGATE:
ifnet_lock_shared(ifp);
ifr->ifr_delegated = ((ifp->if_delegated.ifp != NULL) ?
ifp->if_delegated.ifp->if_index : 0);
ifnet_lock_done(ifp);
break;
case SIOCGIFEXPENSIVE:
ifnet_lock_shared(ifp);
if (ifp->if_eflags & IFEF_EXPENSIVE) {
ifr->ifr_expensive = 1;
} else {
ifr->ifr_expensive = 0;
}
ifnet_lock_done(ifp);
break;
case SIOCSIFEXPENSIVE:
{
struct ifnet *difp;
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_expensive) {
if_set_eflags(ifp, IFEF_EXPENSIVE);
} else {
if_clear_eflags(ifp, IFEF_EXPENSIVE);
}
ifnet_increment_generation(ifp);
/*
* Update the expensive bit in the delegated interface
* structure.
*/
ifnet_head_lock_shared();
TAILQ_FOREACH(difp, &ifnet_head, if_link) {
ifnet_lock_exclusive(difp);
if (difp->if_delegated.ifp == ifp) {
difp->if_delegated.expensive =
ifp->if_eflags & IFEF_EXPENSIVE ? 1 : 0;
ifnet_increment_generation(difp);
}
ifnet_lock_done(difp);
}
ifnet_head_done();
necp_update_all_clients();
break;
}
case SIOCGIFCONSTRAINED:
if ((ifp->if_xflags & IFXF_CONSTRAINED) != 0) {
ifr->ifr_constrained = 1;
} else {
ifr->ifr_constrained = 0;
}
break;
case SIOCSIFCONSTRAINED:
{
struct ifnet *difp;
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_constrained) {
if_set_xflags(ifp, IFXF_CONSTRAINED);
} else {
if_clear_xflags(ifp, IFXF_CONSTRAINED);
}
ifnet_increment_generation(ifp);
/*
* Update the constrained bit in the delegated interface
* structure.
*/
ifnet_head_lock_shared();
TAILQ_FOREACH(difp, &ifnet_head, if_link) {
ifnet_lock_exclusive(difp);
if (difp->if_delegated.ifp == ifp) {
difp->if_delegated.constrained =
((ifp->if_xflags & IFXF_CONSTRAINED) != 0) ? 1 : 0;
ifnet_increment_generation(difp);
}
ifnet_lock_done(difp);
}
ifnet_head_done();
necp_update_all_clients();
break;
}
case SIOCGIF2KCL:
ifnet_lock_shared(ifp);
if (ifp->if_eflags & IFEF_2KCL) {
ifr->ifr_2kcl = 1;
} else {
ifr->ifr_2kcl = 0;
}
ifnet_lock_done(ifp);
break;
case SIOCSIF2KCL:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_2kcl) {
if_set_eflags(ifp, IFEF_2KCL);
} else {
if_clear_eflags(ifp, IFEF_2KCL);
}
break;
case SIOCGSTARTDELAY:
ifnet_lock_shared(ifp);
if (ifp->if_eflags & IFEF_ENQUEUE_MULTI) {
ifr->ifr_start_delay_qlen =
ifp->if_start_delay_qlen;
ifr->ifr_start_delay_timeout =
ifp->if_start_delay_timeout;
} else {
ifr->ifr_start_delay_qlen = 0;
ifr->ifr_start_delay_timeout = 0;
}
ifnet_lock_done(ifp);
break;
case SIOCSIFDSTADDR:
case SIOCSIFADDR:
case SIOCSIFBRDADDR:
case SIOCSIFNETMASK:
case OSIOCGIFADDR:
case OSIOCGIFDSTADDR:
case OSIOCGIFBRDADDR:
case OSIOCGIFNETMASK:
case SIOCSIFKPI:
VERIFY(so->so_proto != NULL);
if (cmd == SIOCSIFDSTADDR || cmd == SIOCSIFADDR ||
cmd == SIOCSIFBRDADDR || cmd == SIOCSIFNETMASK) {
#if BYTE_ORDER != BIG_ENDIAN
if (ifr->ifr_addr.sa_family == 0 &&
ifr->ifr_addr.sa_len < 16) {
ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
ifr->ifr_addr.sa_len = 16;
}
#else
if (ifr->ifr_addr.sa_len == 0) {
ifr->ifr_addr.sa_len = 16;
}
#endif
} else if (cmd == OSIOCGIFADDR) {
cmd = SIOCGIFADDR; /* struct ifreq */
} else if (cmd == OSIOCGIFDSTADDR) {
cmd = SIOCGIFDSTADDR; /* struct ifreq */
} else if (cmd == OSIOCGIFBRDADDR) {
cmd = SIOCGIFBRDADDR; /* struct ifreq */
} else if (cmd == OSIOCGIFNETMASK) {
cmd = SIOCGIFNETMASK; /* struct ifreq */
}
socket_lock(so, 1);
error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd,
(caddr_t)ifr, ifp, p));
socket_unlock(so, 1);
switch (ocmd) {
case OSIOCGIFADDR:
case OSIOCGIFDSTADDR:
case OSIOCGIFBRDADDR:
case OSIOCGIFNETMASK:
bcopy(&ifr->ifr_addr.sa_family, &ifr->ifr_addr,
sizeof(u_short));
}
if (cmd == SIOCSIFKPI) {
int temperr = proc_suser(p);
if (temperr != 0) {
error = temperr;
}
}
// Don't allow to call SIOCSIFADDR and SIOCSIFDSTADDR
// with ifreq as the code expects ifaddr
if ((error == EOPNOTSUPP || error == ENOTSUP) &&
!(cmd == SIOCSIFADDR || cmd == SIOCSIFDSTADDR)) {
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd,
(caddr_t)ifr);
}
break;
case SIOCGIFINTERFACESTATE:
if_get_state(ifp, &ifr->ifr_interface_state);
break;
case SIOCSIFINTERFACESTATE:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
error = if_state_update(ifp, &ifr->ifr_interface_state);
break;
case SIOCSIFPROBECONNECTIVITY:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
error = if_probe_connectivity(ifp,
ifr->ifr_probe_connectivity);
break;
case SIOCGIFPROBECONNECTIVITY:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifp->if_eflags & IFEF_PROBE_CONNECTIVITY) {
ifr->ifr_probe_connectivity = 1;
} else {
ifr->ifr_probe_connectivity = 0;
}
break;
case SIOCGECNMODE:
if ((ifp->if_eflags & (IFEF_ECN_ENABLE | IFEF_ECN_DISABLE)) ==
IFEF_ECN_ENABLE) {
ifr->ifr_ecn_mode = IFRTYPE_ECN_ENABLE;
} else if ((ifp->if_eflags & (IFEF_ECN_ENABLE | IFEF_ECN_DISABLE)) ==
IFEF_ECN_DISABLE) {
ifr->ifr_ecn_mode = IFRTYPE_ECN_DISABLE;
} else {
ifr->ifr_ecn_mode = IFRTYPE_ECN_DEFAULT;
}
break;
case SIOCSECNMODE:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_ecn_mode == IFRTYPE_ECN_DEFAULT) {
if_clear_eflags(ifp, IFEF_ECN_ENABLE | IFEF_ECN_DISABLE);
} else if (ifr->ifr_ecn_mode == IFRTYPE_ECN_ENABLE) {
if_set_eflags(ifp, IFEF_ECN_ENABLE);
if_clear_eflags(ifp, IFEF_ECN_DISABLE);
} else if (ifr->ifr_ecn_mode == IFRTYPE_ECN_DISABLE) {
if_set_eflags(ifp, IFEF_ECN_DISABLE);
if_clear_eflags(ifp, IFEF_ECN_ENABLE);
} else {
error = EINVAL;
}
break;
case SIOCSIFTIMESTAMPENABLE:
case SIOCSIFTIMESTAMPDISABLE:
error = proc_suser(p);
if (error != 0) {
break;
}
if ((cmd == SIOCSIFTIMESTAMPENABLE &&
(ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) != 0) ||
(cmd == SIOCSIFTIMESTAMPDISABLE &&
(ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) == 0)) {
break;
}
if (cmd == SIOCSIFTIMESTAMPENABLE) {
if_set_xflags(ifp, IFXF_TIMESTAMP_ENABLED);
} else {
if_clear_xflags(ifp, IFXF_TIMESTAMP_ENABLED);
}
/*
* Pass the setting to the interface if it supports either
* software or hardware time stamping
*/
if (ifp->if_capabilities & (IFCAP_HW_TIMESTAMP |
IFCAP_SW_TIMESTAMP)) {
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd,
(caddr_t)ifr);
}
break;
case SIOCGIFTIMESTAMPENABLED: {
if ((ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) != 0) {
ifr->ifr_intval = 1;
} else {
ifr->ifr_intval = 0;
}
break;
}
case SIOCSQOSMARKINGMODE:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
error = if_set_qosmarking_mode(ifp, ifr->ifr_qosmarking_mode);
break;
case SIOCGQOSMARKINGMODE:
ifr->ifr_qosmarking_mode = ifp->if_qosmarking_mode;
break;
case SIOCSQOSMARKINGENABLED:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_qosmarking_enabled != 0) {
if_set_eflags(ifp, IFEF_QOSMARKING_ENABLED);
} else {
if_clear_eflags(ifp, IFEF_QOSMARKING_ENABLED);
}
break;
case SIOCGQOSMARKINGENABLED:
ifr->ifr_qosmarking_enabled =
((ifp->if_eflags & IFEF_QOSMARKING_ENABLED) != 0) ? 1 : 0;
break;
case SIOCSIFDISABLEOUTPUT:
#if (DEBUG || DEVELOPMENT)
if (ifr->ifr_disable_output == 1) {
error = ifnet_disable_output(ifp);
} else if (ifr->ifr_disable_output == 0) {
error = ifnet_enable_output(ifp);
} else {
error = EINVAL;
}
#else
error = EINVAL;
#endif /* (DEBUG || DEVELOPMENT) */
break;
case SIOCSIFSUBFAMILY:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
error = ifnet_ioctl(ifp, SOCK_DOM(so), cmd, (caddr_t)ifr);
break;
case SIOCSIFLOWINTERNET:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_low_internet & IFRTYPE_LOW_INTERNET_ENABLE_UL) {
if_set_xflags(ifp, IFXF_LOW_INTERNET_UL);
} else {
if_clear_xflags(ifp, IFXF_LOW_INTERNET_UL);
}
if (ifr->ifr_low_internet & IFRTYPE_LOW_INTERNET_ENABLE_DL) {
if_set_xflags(ifp, IFXF_LOW_INTERNET_DL);
} else {
if_clear_xflags(ifp, IFXF_LOW_INTERNET_DL);
}
break;
case SIOCGIFLOWINTERNET:
ifnet_lock_shared(ifp);
ifr->ifr_low_internet = 0;
if ((ifp->if_xflags & IFXF_LOW_INTERNET_UL) != 0) {
ifr->ifr_low_internet |=
IFRTYPE_LOW_INTERNET_ENABLE_UL;
}
if ((ifp->if_xflags & IFXF_LOW_INTERNET_DL) != 0) {
ifr->ifr_low_internet |=
IFRTYPE_LOW_INTERNET_ENABLE_DL;
}
ifnet_lock_done(ifp);
break;
case SIOCGIFLOWPOWER:
ifr->ifr_low_power_mode =
((ifp->if_xflags & IFXF_LOW_POWER) != 0);
break;
case SIOCSIFLOWPOWER:
#if (DEVELOPMENT || DEBUG)
error = if_set_low_power(ifp, (ifr->ifr_low_power_mode != 0));
#else /* DEVELOPMENT || DEBUG */
error = EOPNOTSUPP;
#endif /* DEVELOPMENT || DEBUG */
break;
case SIOCGIFMPKLOG:
ifr->ifr_mpk_log = ((ifp->if_xflags & IFXF_MPK_LOG) != 0);
break;
case SIOCSIFMPKLOG:
if (ifr->ifr_mpk_log) {
if_set_xflags(ifp, IFXF_MPK_LOG);
} else {
if_clear_xflags(ifp, IFXF_MPK_LOG);
}
break;
case SIOCGIFNOACKPRIO:
if ((ifp->if_eflags & IFEF_NOACKPRI) != 0) {
ifr->ifr_noack_prio = 1;
} else {
ifr->ifr_noack_prio = 0;
}
break;
case SIOCSIFNOACKPRIO:
if ((error = priv_check_cred(kauth_cred_get(),
PRIV_NET_INTERFACE_CONTROL, 0)) != 0) {
return error;
}
if (ifr->ifr_noack_prio) {
if_set_eflags(ifp, IFEF_NOACKPRI);
} else {
if_clear_eflags(ifp, IFEF_NOACKPRI);
}
break;
default:
VERIFY(0);
/* NOTREACHED */
}
return error;
}