static __attribute__()

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;
}