/*
- * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2018 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
SYSCTL_INT(_net_link_generic_system, OID_AUTO, if_verbose,
CTLFLAG_RW | CTLFLAG_LOCKED, &if_verbose, 0, "");
+boolean_t intcoproc_unrestricted;
+
/* Eventhandler context for interface events */
struct eventhandler_lists_ctxt ifnet_evhdlr_ctxt;
lck_mtx_init(&ifma_trash_lock, ifa_mtx_grp, ifa_mtx_attr);
TAILQ_INIT(&ifma_trash_head);
+
+ PE_parse_boot_argn("intcoproc_unrestricted", &intcoproc_unrestricted,
+ sizeof (intcoproc_unrestricted));
}
/*
if (ifa->ifa_attached != NULL)
(*ifa->ifa_attached)(ifa);
+
}
__private_extern__ void
for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
bzero(outbuf, sizeof(outbuf));
- strlcpy(outbuf, ifc->ifc_name,
- min(strlen(ifc->ifc_name), IFNAMSIZ));
+ strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
error = copyout(outbuf, dst, IFNAMSIZ);
if (error)
break;
/*
* Find an interface address specific to an interface best matching
- * a given address.
+ * a given address applying same source address selection rules
+ * as done in the kernel for implicit source address binding
+ */
+struct ifaddr *
+ifaof_ifpforaddr_select(const struct sockaddr *addr, struct ifnet *ifp)
+{
+ u_int af = addr->sa_family;
+
+ if (af == AF_INET6)
+ return (in6_selectsrc_core_ifa(__DECONST(struct sockaddr_in6 *, addr), ifp, 0));
+
+ return (ifaof_ifpforaddr(addr, ifp));
+}
+
+/*
+ * Find an interface address specific to an interface best matching
+ * a given address without regards to source address selection.
+ *
+ * This is appropriate for use-cases where we just want to update/init
+ * some data structure like routing table entries.
*/
struct ifaddr *
ifaof_ifpforaddr(const struct sockaddr *addr, struct ifnet *ifp)
}
ifnet_touch_lastchange(ifp);
+ ifnet_touch_lastupdown(ifp);
/* Drop the lock to notify addresses and route */
ifnet_lock_done(ifp);
struct tb_profile tb = { 0, 0, 0 };
if ((error = proc_suser(p)) != 0)
- break;
+ break;
+
IFCQ_LOCK(ifq);
if (!IFCQ_IS_READY(ifq)) {
return (error);
}
+boolean_t
+if_check_netagent(struct ifnet *ifp, uuid_t find_agent_uuid)
+{
+ boolean_t found = FALSE;
+
+ if (!ifp || uuid_is_null(find_agent_uuid))
+ return FALSE;
+
+ ifnet_lock_shared(ifp);
+
+ if (ifp->if_agentids != NULL) {
+ for (uint32_t index = 0; index < ifp->if_agentcount; index++) {
+ if (uuid_compare(ifp->if_agentids[index], find_agent_uuid) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ ifnet_lock_done(ifp);
+
+ return found;
+}
+
static __attribute__((noinline)) int
ifioctl_netagent(struct ifnet *ifp, u_long cmd, caddr_t data, struct proc *p)
{
case SIOCSIFORDER: { /* struct if_order */
struct if_order *ifo = (struct if_order *)(void *)data;
- if (ifo->ifo_count == 0 || ifo->ifo_count > (u_int32_t)if_index) {
+ if (ifo->ifo_count > (u_int32_t)if_index) {
error = EINVAL;
break;
}
if (error != 0) {
break;
}
- }
- /* ordered_indices should not contain duplicates */
- bool found_duplicate = FALSE;
- for (uint32_t i = 0; i < (ifo->ifo_count - 1) && !found_duplicate ; i++){
- for (uint32_t j = i + 1; j < ifo->ifo_count && !found_duplicate ; j++){
- if (ordered_indices[j] == ordered_indices[i]){
- error = EINVAL;
- found_duplicate = TRUE;
- break;
+
+ /* ordered_indices should not contain duplicates */
+ bool found_duplicate = FALSE;
+ for (uint32_t i = 0; i < (ifo->ifo_count - 1) && !found_duplicate ; i++){
+ for (uint32_t j = i + 1; j < ifo->ifo_count && !found_duplicate ; j++){
+ if (ordered_indices[j] == ordered_indices[i]){
+ error = EINVAL;
+ found_duplicate = TRUE;
+ break;
+ }
}
}
+ if (found_duplicate) {
+ break;
+ }
}
- if (found_duplicate)
- break;
error = ifnet_reset_order(ordered_indices, ifo->ifo_count);
case SIOCGIFORDER: { /* struct if_order */
struct if_order *ifo = (struct if_order *)(void *)data;
-
- u_int32_t ordered_count = if_ordered_count;
+ u_int32_t ordered_count = *((volatile u_int32_t *)&if_ordered_count);
if (ifo->ifo_count == 0 ||
ordered_count == 0) {
- ifo->ifo_count = ordered_count;
+ ifo->ifo_count = 0;
} else if (ifo->ifo_ordered_indices != USER_ADDR_NULL) {
u_int32_t count_to_copy =
MIN(ordered_count, ifo->ifo_count);
struct ifnet *ifp = NULL;
u_int32_t cursor = 0;
- ordered_indices = _MALLOC(length, M_NECP, M_WAITOK);
+ ordered_indices = _MALLOC(length, M_NECP, M_WAITOK | M_ZERO);
if (ordered_indices == NULL) {
error = ENOMEM;
break;
ifnet_head_lock_shared();
TAILQ_FOREACH(ifp, &ifnet_ordered_head, if_ordered_link) {
- if (cursor >= count_to_copy) {
+ if (cursor >= count_to_copy ||
+ cursor >= if_ordered_count) {
break;
}
ordered_indices[cursor] = ifp->if_index;
}
ifnet_head_done();
- ifo->ifo_count = count_to_copy;
+ /* We might have parsed less than the original length
+ * because the list could have changed.
+ */
+ length = cursor * sizeof(u_int32_t);
+ ifo->ifo_count = cursor;
error = copyout(ordered_indices,
ifo->ifo_ordered_indices, length);
} else {
#endif
+static int
+ifioctl_get_protolist(struct ifnet *ifp, u_int32_t * ret_count,
+ user_addr_t ifpl)
+{
+ u_int32_t actual_count;
+ u_int32_t count;
+ int error = 0;
+ u_int32_t *list = NULL;
+
+ /* find out how many */
+ count = if_get_protolist(ifp, NULL, 0);
+ if (ifpl == USER_ADDR_NULL) {
+ goto done;
+ }
+
+ /* copy out how many there's space for */
+ if (*ret_count < count) {
+ count = *ret_count;
+ }
+ if (count == 0) {
+ goto done;
+ }
+ list = _MALLOC(count * sizeof(*list), M_TEMP, M_WAITOK);
+ if (list == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ actual_count = if_get_protolist(ifp, list, count);
+ if (actual_count < count) {
+ count = actual_count;
+ }
+ if (count != 0) {
+ error = copyout((caddr_t)list, ifpl, count * sizeof(*list));
+ }
+
+ done:
+ if (list != NULL) {
+ if_free_protolist(list);
+ }
+ *ret_count = count;
+ return (error);
+}
+
+static __attribute__((noinline)) int
+ifioctl_protolist(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case SIOCGIFPROTOLIST32: { /* struct if_protolistreq32 */
+ struct if_protolistreq32 ifpl;
+
+ bcopy(data, &ifpl, sizeof(ifpl));
+ if (ifpl.ifpl_reserved != 0) {
+ error = EINVAL;
+ break;
+ }
+ error = ifioctl_get_protolist(ifp, &ifpl.ifpl_count,
+ CAST_USER_ADDR_T(ifpl.ifpl_list));
+ bcopy(&ifpl, data, sizeof(ifpl));
+ break;
+ }
+ case SIOCGIFPROTOLIST64: { /* struct if_protolistreq64 */
+ struct if_protolistreq64 ifpl;
+
+ bcopy(data, &ifpl, sizeof(ifpl));
+ if (ifpl.ifpl_reserved != 0) {
+ error = EINVAL;
+ break;
+ }
+ error = ifioctl_get_protolist(ifp, &ifpl.ifpl_count,
+ ifpl.ifpl_list);
+ bcopy(&ifpl, data, sizeof(ifpl));
+ break;
+ }
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ return (error);
+}
+
+/*
+ * List the ioctl()s we can perform on restricted INTCOPROC interfaces.
+ */
+static bool
+ifioctl_restrict_intcoproc(unsigned long cmd, const char *ifname,
+ struct ifnet *ifp, struct proc *p)
+{
+
+ if (intcoproc_unrestricted == TRUE) {
+ return (false);
+ }
+ if (proc_pid(p) == 0) {
+ return (false);
+ }
+ if (ifname) {
+ ifp = ifunit(ifname);
+ }
+ if (ifp == NULL) {
+ return (false);
+ }
+ if (!IFNET_IS_INTCOPROC(ifp)) {
+ return (false);
+ }
+ switch (cmd) {
+ case SIOCGIFBRDADDR:
+ case SIOCGIFCONF32:
+ case SIOCGIFCONF64:
+ case SIOCGIFFLAGS:
+ case SIOCGIFEFLAGS:
+ case SIOCGIFCAP:
+ case SIOCGIFMAC:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFPHYS:
+ case SIOCGIFTYPE:
+ case SIOCGIFFUNCTIONALTYPE:
+ case SIOCGIFPSRCADDR:
+ case SIOCGIFPDSTADDR:
+ case SIOCGIFGENERIC:
+ case SIOCGIFDEVMTU:
+ case SIOCGIFVLAN:
+ case SIOCGIFBOND:
+ case SIOCGIFWAKEFLAGS:
+ case SIOCGIFGETRTREFCNT:
+ case SIOCGIFOPPORTUNISTIC:
+ case SIOCGIFLINKQUALITYMETRIC:
+ case SIOCGIFLOG:
+ case SIOCGIFDELEGATE:
+ case SIOCGIFEXPENSIVE:
+ case SIOCGIFINTERFACESTATE:
+ case SIOCGIFPROBECONNECTIVITY:
+ case SIOCGIFTIMESTAMPENABLED:
+ case SIOCGECNMODE:
+ case SIOCGQOSMARKINGMODE:
+ case SIOCGQOSMARKINGENABLED:
+ case SIOCGIFLOWINTERNET:
+ case SIOCGIFSTATUS:
+ case SIOCGIFMEDIA32:
+ case SIOCGIFMEDIA64:
+ case SIOCGIFDESC:
+ case SIOCGIFLINKPARAMS:
+ case SIOCGIFQUEUESTATS:
+ case SIOCGIFTHROTTLE:
+ case SIOCGIFAGENTIDS32:
+ case SIOCGIFAGENTIDS64:
+ case SIOCGIFNETSIGNATURE:
+ case SIOCGIFINFO_IN6:
+ case SIOCGIFAFLAG_IN6:
+ case SIOCGNBRINFO_IN6:
+ case SIOCGIFALIFETIME_IN6:
+ case SIOCGIFNETMASK_IN6:
+ case SIOCGIFPROTOLIST32:
+ case SIOCGIFPROTOLIST64:
+ return (false);
+ default:
+#if (DEBUG || DEVELOPMENT)
+ printf("%s: cmd 0x%lx not allowed (pid %u)\n",
+ __func__, cmd, proc_pid(p));
+#endif
+ return (true);
+ }
+ return (false);
+}
+
/*
* Interface ioctls.
*
bcopy(data, &ifr, sizeof (ifr));
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
bcopy(&ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ifioctl_restrict_intcoproc(cmd, ifname, NULL, p) == true) {
+ error = EPERM;
+ goto done;
+ }
error = ifioctl_ifreq(so, cmd, &ifr, p);
bcopy(&ifr, data, sizeof (ifr));
goto done;
ifp = ifunit(ifname);
break;
+ case SIOCGIFPROTOLIST32: /* struct if_protolistreq32 */
+ case SIOCGIFPROTOLIST64: /* struct if_protolistreq64 */
+ bcopy(((struct if_protolistreq *)(void *)data)->ifpl_name,
+ ifname, IFNAMSIZ);
+ ifp = ifunit(ifname);
+ break;
default:
/*
* This is a bad assumption, but the code seems to
goto done;
}
+ if (ifioctl_restrict_intcoproc(cmd, NULL, ifp, p) == true) {
+ error = EPERM;
+ goto done;
+ }
switch (cmd) {
case SIOCSIFPHYADDR: /* struct {if,in_}aliasreq */
#if INET6
error = ifioctl_nat64prefix(ifp, cmd, data);
break;
#endif
+
+ case SIOCGIFPROTOLIST32: /* struct if_protolistreq32 */
+ case SIOCGIFPROTOLIST64: /* struct if_protolistreq64 */
+ error = ifioctl_protolist(ifp, cmd, data);
+ break;
+
default:
if (so->so_proto == NULL) {
error = EOPNOTSUPP;
* Send the event even upon error from the driver because
* we changed the flags.
*/
- 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_SIFFLAGS;
- 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);
+ dlil_post_sifflags_msg(ifp);
ifnet_touch_lastchange(ifp);
break;
case SIOCSQOSMARKINGENABLED:
case SIOCGQOSMARKINGMODE:
case SIOCGQOSMARKINGENABLED:
+
+ case SIOCGIFPROTOLIST32:
+ case SIOCGIFPROTOLIST64:
;
}
}
-/*
- * XXX: This API is only used by BSD stack and for now will always return 0.
- * For Skywalk native drivers, preamble space need not be allocated in mbuf
- * as the preamble will be reserved in the translated skywalk packet
- * which is transmitted to the driver.
- * For Skywalk compat drivers currently headroom is always set to zero.
- */
uint32_t
ifnet_mbuf_packetpreamblelen(struct ifnet *ifp)
{