+static __attribute__((noinline)) int
+ifioctl_ifconf(u_long cmd, caddr_t data)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case OSIOCGIFCONF32: /* struct ifconf32 */
+ case SIOCGIFCONF32: { /* struct ifconf32 */
+ struct ifconf32 ifc;
+ bcopy(data, &ifc, sizeof (ifc));
+ error = ifconf(cmd, CAST_USER_ADDR_T(ifc.ifc_req),
+ &ifc.ifc_len);
+ bcopy(&ifc, data, sizeof (ifc));
+ break;
+ }
+
+ case SIOCGIFCONF64: /* struct ifconf64 */
+ case OSIOCGIFCONF64: { /* struct ifconf64 */
+ struct ifconf64 ifc;
+ bcopy(data, &ifc, sizeof (ifc));
+ error = ifconf(cmd, ifc.ifc_req, &ifc.ifc_len);
+ bcopy(&ifc, data, sizeof (ifc));
+ break;
+ }
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+static __attribute__((noinline)) int
+ifioctl_ifclone(u_long cmd, caddr_t data)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case SIOCIFGCLONERS32: { /* struct if_clonereq32 */
+ struct if_clonereq32 ifcr;
+ bcopy(data, &ifcr, sizeof (ifcr));
+ error = if_clone_list(ifcr.ifcr_count, &ifcr.ifcr_total,
+ CAST_USER_ADDR_T(ifcr.ifcru_buffer));
+ bcopy(&ifcr, data, sizeof (ifcr));
+ break;
+ }
+
+ case SIOCIFGCLONERS64: { /* struct if_clonereq64 */
+ struct if_clonereq64 ifcr;
+ bcopy(data, &ifcr, sizeof (ifcr));
+ error = if_clone_list(ifcr.ifcr_count, &ifcr.ifcr_total,
+ ifcr.ifcru_buffer);
+ bcopy(&ifcr, data, sizeof (ifcr));
+ break;
+ }
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+static __attribute__((noinline)) int
+ifioctl_ifdesc(struct ifnet *ifp, u_long cmd, caddr_t data, struct proc *p)
+{
+ struct if_descreq *ifdr = (struct if_descreq *)(void *)data;
+ u_int32_t ifdr_len;
+ int error = 0;
+
+ VERIFY(ifp != NULL);
+
+ switch (cmd) {
+ case SIOCSIFDESC: { /* struct if_descreq */
+ if ((error = proc_suser(p)) != 0)
+ break;
+
+ ifnet_lock_exclusive(ifp);
+ bcopy(&ifdr->ifdr_len, &ifdr_len, sizeof (ifdr_len));
+ if (ifdr_len > sizeof (ifdr->ifdr_desc) ||
+ ifdr_len > ifp->if_desc.ifd_maxlen) {
+ error = EINVAL;
+ ifnet_lock_done(ifp);
+ break;
+ }
+
+ bzero(ifp->if_desc.ifd_desc, ifp->if_desc.ifd_maxlen);
+ if ((ifp->if_desc.ifd_len = ifdr_len) > 0) {
+ bcopy(ifdr->ifdr_desc, ifp->if_desc.ifd_desc,
+ MIN(ifdr_len, ifp->if_desc.ifd_maxlen));
+ }
+ ifnet_lock_done(ifp);
+ break;
+ }
+
+ case SIOCGIFDESC: { /* struct if_descreq */
+ ifnet_lock_shared(ifp);
+ ifdr_len = MIN(ifp->if_desc.ifd_len, sizeof (ifdr->ifdr_desc));
+ bcopy(&ifdr_len, &ifdr->ifdr_len, sizeof (ifdr_len));
+ bzero(&ifdr->ifdr_desc, sizeof (ifdr->ifdr_desc));
+ if (ifdr_len > 0) {
+ bcopy(ifp->if_desc.ifd_desc, ifdr->ifdr_desc, ifdr_len);
+ }
+ ifnet_lock_done(ifp);
+ break;
+ }
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+static __attribute__((noinline)) int
+ifioctl_linkparams(struct ifnet *ifp, u_long cmd, caddr_t data, struct proc *p)
+{
+ struct if_linkparamsreq *iflpr =
+ (struct if_linkparamsreq *)(void *)data;
+ struct ifclassq *ifq;
+ int error = 0;
+
+ VERIFY(ifp != NULL);
+ ifq = &ifp->if_snd;
+
+ switch (cmd) {
+ case SIOCSIFLINKPARAMS: { /* struct if_linkparamsreq */
+ struct tb_profile tb = { 0, 0, 0 };
+
+ if ((error = proc_suser(p)) != 0)
+ break;
+
+ IFCQ_LOCK(ifq);
+ if (!IFCQ_IS_READY(ifq)) {
+ error = ENXIO;
+ IFCQ_UNLOCK(ifq);
+ break;
+ }
+ bcopy(&iflpr->iflpr_output_tbr_rate, &tb.rate,
+ sizeof (tb.rate));
+ bcopy(&iflpr->iflpr_output_tbr_percent, &tb.percent,
+ sizeof (tb.percent));
+ error = ifclassq_tbr_set(ifq, &tb, TRUE);
+ IFCQ_UNLOCK(ifq);
+ break;
+ }
+
+ case SIOCGIFLINKPARAMS: { /* struct if_linkparamsreq */
+ u_int32_t sched_type = PKTSCHEDT_NONE, flags = 0;
+ u_int64_t tbr_bw = 0, tbr_pct = 0;
+
+ IFCQ_LOCK(ifq);
+#if PF_ALTQ
+ if (ALTQ_IS_ENABLED(IFCQ_ALTQ(ifq))) {
+ sched_type = IFCQ_ALTQ(ifq)->altq_type;
+ flags |= IFLPRF_ALTQ;
+ } else
+#endif /* PF_ALTQ */
+ {
+ if (IFCQ_IS_ENABLED(ifq))
+ sched_type = ifq->ifcq_type;
+ }
+ bcopy(&sched_type, &iflpr->iflpr_output_sched,
+ sizeof (iflpr->iflpr_output_sched));
+
+ if (IFCQ_TBR_IS_ENABLED(ifq)) {
+ tbr_bw = ifq->ifcq_tbr.tbr_rate_raw;
+ tbr_pct = ifq->ifcq_tbr.tbr_percent;
+ }
+ bcopy(&tbr_bw, &iflpr->iflpr_output_tbr_rate,
+ sizeof (iflpr->iflpr_output_tbr_rate));
+ bcopy(&tbr_pct, &iflpr->iflpr_output_tbr_percent,
+ sizeof (iflpr->iflpr_output_tbr_percent));
+ IFCQ_UNLOCK(ifq);
+
+ if (ifp->if_output_sched_model ==
+ IFNET_SCHED_MODEL_DRIVER_MANAGED)
+ flags |= IFLPRF_DRVMANAGED;
+ bcopy(&flags, &iflpr->iflpr_flags, sizeof (iflpr->iflpr_flags));
+ bcopy(&ifp->if_output_bw, &iflpr->iflpr_output_bw,
+ sizeof (iflpr->iflpr_output_bw));
+ bcopy(&ifp->if_input_bw, &iflpr->iflpr_input_bw,
+ sizeof (iflpr->iflpr_input_bw));
+ bcopy(&ifp->if_output_lt, &iflpr->iflpr_output_lt,
+ sizeof (iflpr->iflpr_output_lt));
+ bcopy(&ifp->if_input_lt, &iflpr->iflpr_input_lt,
+ sizeof (iflpr->iflpr_input_lt));
+ break;
+ }
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+static __attribute__((noinline)) int
+ifioctl_qstats(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct if_qstatsreq *ifqr = (struct if_qstatsreq *)(void *)data;
+ u_int32_t ifqr_len, ifqr_slot;
+ int error = 0;
+
+ VERIFY(ifp != NULL);
+
+ switch (cmd) {
+ case SIOCGIFQUEUESTATS: { /* struct if_qstatsreq */
+ bcopy(&ifqr->ifqr_slot, &ifqr_slot, sizeof (ifqr_slot));
+ bcopy(&ifqr->ifqr_len, &ifqr_len, sizeof (ifqr_len));
+ error = ifclassq_getqstats(&ifp->if_snd, ifqr_slot,
+ ifqr->ifqr_buf, &ifqr_len);
+ if (error != 0)
+ ifqr_len = 0;
+ bcopy(&ifqr_len, &ifqr->ifqr_len, sizeof (ifqr_len));
+ break;
+ }
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+static __attribute__((noinline)) int
+ifioctl_throttle(struct ifnet *ifp, u_long cmd, caddr_t data, struct proc *p)
+{
+ struct if_throttlereq *ifthr = (struct if_throttlereq *)(void *)data;
+ u_int32_t ifthr_level;
+ int error = 0;
+
+ VERIFY(ifp != NULL);
+
+ switch (cmd) {
+ case SIOCSIFTHROTTLE: { /* struct if_throttlereq */
+ /*
+ * XXX: Use priv_check_cred() instead of root check?
+ */
+ if ((error = proc_suser(p)) != 0)
+ break;
+
+ bcopy(&ifthr->ifthr_level, &ifthr_level, sizeof (ifthr_level));
+ error = ifnet_set_throttle(ifp, ifthr_level);
+ if (error == EALREADY)
+ error = 0;
+ break;
+ }
+
+ case SIOCGIFTHROTTLE: { /* struct if_throttlereq */
+ if ((error = ifnet_get_throttle(ifp, &ifthr_level)) == 0) {
+ bcopy(&ifthr_level, &ifthr->ifthr_level,
+ sizeof (ifthr_level));
+ }
+ break;
+ }
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+static int
+ifioctl_getnetagents(struct ifnet *ifp, u_int32_t *count, user_addr_t uuid_p)
+{
+ int error = 0;
+ int index = 0;
+ u_int32_t valid_netagent_count = 0;
+ *count = 0;
+ for (index = 0; index < IF_MAXAGENTS; index++) {
+ uuid_t *netagent_uuid = &(ifp->if_agentids[index]);
+ if (!uuid_is_null(*netagent_uuid)) {
+ if (uuid_p != USER_ADDR_NULL) {
+ if ((error = copyout(netagent_uuid,
+ uuid_p + sizeof(uuid_t) * valid_netagent_count,
+ sizeof(uuid_t))) != 0) {
+ return (error);
+ }
+ }
+ valid_netagent_count++;
+ }
+ }
+ *count = valid_netagent_count;
+
+ return (0);
+}
+
+static __attribute__((noinline)) int
+ifioctl_netagent(struct ifnet *ifp, u_long cmd, caddr_t data, struct proc *p)
+{
+ struct if_agentidreq *ifar = (struct if_agentidreq *)(void *)data;
+ union {
+ struct if_agentidsreq32 s32;
+ struct if_agentidsreq64 s64;
+ } u;
+ int error = 0;
+ int index = 0;
+
+ VERIFY(ifp != NULL);
+
+ switch (cmd) {
+ case SIOCAIFAGENTID: { /* struct if_agentidreq */
+ uuid_t *first_empty_slot = NULL;
+ // TODO: Use priv_check_cred() instead of root check
+ if ((error = proc_suser(p)) != 0) {
+ break;
+ }
+ for (index = 0; index < IF_MAXAGENTS; index++) {
+ uuid_t *netagent_uuid = &(ifp->if_agentids[index]);
+ if (uuid_compare(*netagent_uuid, ifar->ifar_uuid) == 0) {
+ /* Already present, ignore */
+ break;
+ }
+ if (first_empty_slot == NULL &&
+ uuid_is_null(*netagent_uuid)) {
+ first_empty_slot = netagent_uuid;
+ }
+ }
+ if (first_empty_slot == NULL) {
+ error = ENOMEM; /* No empty slot for a netagent UUID, bail */
+ break;
+ }
+ uuid_copy(*first_empty_slot, ifar->ifar_uuid);
+ netagent_post_updated_interfaces(ifar->ifar_uuid);
+ break;
+ }
+ case SIOCDIFAGENTID: { /* struct if_agentidreq */
+ bool removed_agent_id = FALSE;
+ // TODO: Use priv_check_cred() instead of root check
+ if ((error = proc_suser(p)) != 0) {
+ break;
+ }
+ for (index = 0; index < IF_MAXAGENTS; index++) {
+ uuid_t *netagent_uuid = &(ifp->if_agentids[index]);
+ if (uuid_compare(*netagent_uuid, ifar->ifar_uuid) == 0) {
+ uuid_clear(*netagent_uuid);
+ removed_agent_id = TRUE;
+ break;
+ }
+ }
+ if (removed_agent_id) {
+ netagent_post_updated_interfaces(ifar->ifar_uuid);
+ }
+ break;
+ }
+ case SIOCGIFAGENTIDS32: { /* struct if_agentidsreq32 */
+ bcopy(data, &u.s32, sizeof(u.s32));
+ error = ifioctl_getnetagents(ifp, &u.s32.ifar_count, u.s32.ifar_uuids);
+ if (error == 0) {
+ bcopy(&u.s32, data, sizeof(u.s32));
+ }
+ break;
+ }
+ case SIOCGIFAGENTIDS64: { /* struct if_agentidsreq64 */
+ bcopy(data, &u.s64, sizeof(u.s64));
+ error = ifioctl_getnetagents(ifp, &u.s64.ifar_count, u.s64.ifar_uuids);
+ if (error == 0) {
+ bcopy(&u.s64, data, sizeof(u.s64));
+ }
+ break;
+ }
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+void
+ifnet_clear_netagent(uuid_t netagent_uuid)
+{
+ struct ifnet *ifp = NULL;
+ int index = 0;
+ bool removed_agent_id = FALSE;
+
+ ifnet_head_lock_shared();
+
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
+ for (index = 0; index < IF_MAXAGENTS; index++) {
+ uuid_t *ifp_netagent_uuid = &(ifp->if_agentids[index]);
+ if (uuid_compare(*ifp_netagent_uuid, netagent_uuid) == 0) {
+ uuid_clear(*ifp_netagent_uuid);
+ removed_agent_id = TRUE;
+ }
+ }
+ }
+
+ ifnet_head_done();
+}
+
+static __attribute__((noinline)) int
+ifioctl_netsignature(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct if_nsreq *ifnsr = (struct if_nsreq *)(void *)data;
+ u_int16_t flags;
+ int error = 0;
+
+ VERIFY(ifp != NULL);
+
+ switch (cmd) {
+ case SIOCSIFNETSIGNATURE: /* struct if_nsreq */
+ if (ifnsr->ifnsr_len > sizeof (ifnsr->ifnsr_data)) {
+ error = EINVAL;
+ break;
+ }
+ bcopy(&ifnsr->ifnsr_flags, &flags, sizeof (flags));
+ error = ifnet_set_netsignature(ifp, ifnsr->ifnsr_family,
+ ifnsr->ifnsr_len, flags, ifnsr->ifnsr_data);
+ break;
+
+ case SIOCGIFNETSIGNATURE: /* struct if_nsreq */
+ ifnsr->ifnsr_len = sizeof (ifnsr->ifnsr_data);
+ error = ifnet_get_netsignature(ifp, ifnsr->ifnsr_family,
+ &ifnsr->ifnsr_len, &flags, ifnsr->ifnsr_data);
+ if (error == 0)
+ bcopy(&flags, &ifnsr->ifnsr_flags, sizeof (flags));
+ else
+ ifnsr->ifnsr_len = 0;
+ break;
+
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}