/*
- * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/kpi_mbuf.h>
#include <sys/mcache.h>
#include <sys/protosw.h>
+#include <sys/syslog.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/dlil.h>
#include <net/if_dl.h>
#include <net/if_arp.h>
#include <net/if_llreach.h>
+#include <net/if_ether.h>
#include <net/route.h>
#include <libkern/libkern.h>
#include <libkern/OSAtomic.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#include <netinet/in_pcb.h>
#ifdef INET
#include <netinet/igmp_var.h>
#endif
#ifdef INET6
#include <netinet6/mld6_var.h>
#endif
+#include <netkey/key.h>
#include "net/net_str_id.h"
-#if IF_LASTCHANGEUPTIME
+#if CONFIG_MACF
+#include <sys/kauth.h>
+#include <security/mac_framework.h>
+#endif
+
#define TOUCHLASTCHANGE(__if_lastchange) { \
(__if_lastchange)->tv_sec = net_uptime(); \
(__if_lastchange)->tv_usec = 0; \
}
-#else
-#define TOUCHLASTCHANGE(__if_lastchange) microtime(__if_lastchange)
-#endif
-
-#define _cast_non_const(p) ((void *)(uintptr_t)(p))
static errno_t ifnet_defrouter_llreachinfo(ifnet_t, int,
struct ifnet_llreach_info *);
{
struct ifnet_init_eparams einit;
struct ifnet *ifp = NULL;
+ char if_xname[IFXNAMSIZ] = {0};
int error;
einit = *einit0;
}
}
+ if (einit.uniqueid == NULL) {
+ /* Initialize external name (name + unit) */
+ snprintf(if_xname, IFXNAMSIZ,
+ "%s%d", einit.name, einit.unit);
+ einit.uniqueid = if_xname;
+ einit.uniqueid_len = strlen(if_xname);
+ }
+
error = dlil_if_acquire(einit.family, einit.uniqueid,
einit.uniqueid_len, &ifp);
* to point to storage of at least IFNAMSIZ bytes. It is safe
* to write to this.
*/
- strncpy(_cast_non_const(ifp->if_name), einit.name, IFNAMSIZ);
+ strlcpy(__DECONST(char *, ifp->if_name), einit.name, IFNAMSIZ);
ifp->if_type = einit.type;
ifp->if_family = einit.family;
+ ifp->if_subfamily = einit.subfamily;
ifp->if_unit = einit.unit;
ifp->if_output = einit.output;
ifp->if_pre_enqueue = einit.pre_enqueue;
ifp->if_output_sched_model = einit.output_sched_model;
ifp->if_output_bw.eff_bw = einit.output_bw;
ifp->if_output_bw.max_bw = einit.output_bw_max;
+ ifp->if_output_lt.eff_lt = einit.output_lt;
+ ifp->if_output_lt.max_lt = einit.output_lt_max;
ifp->if_input_poll = einit.input_poll;
ifp->if_input_ctl = einit.input_ctl;
ifp->if_input_bw.eff_bw = einit.input_bw;
ifp->if_input_bw.max_bw = einit.input_bw_max;
+ ifp->if_input_lt.eff_lt = einit.input_lt;
+ ifp->if_input_lt.max_lt = einit.input_lt_max;
ifp->if_demux = einit.demux;
ifp->if_add_proto = einit.add_proto;
ifp->if_del_proto = einit.del_proto;
ifp->if_check_multi = einit.check_multi;
- ifp->if_framer = einit.framer;
+ ifp->if_framer_legacy = einit.framer;
+ ifp->if_framer = einit.framer_extended;
ifp->if_softc = einit.softc;
ifp->if_ioctl = einit.ioctl;
ifp->if_set_bpf_tap = einit.set_bpf_tap;
ifp->if_event = einit.event;
ifp->if_kpi_storage = einit.detach;
+ /* Initialize external name (name + unit) */
+ snprintf(__DECONST(char *, ifp->if_xname), IFXNAMSIZ,
+ "%s%d", ifp->if_name, ifp->if_unit);
+
+ /*
+ * On embedded, framer() is already in the extended form;
+ * we simply use it as is, unless the caller specifies
+ * framer_extended() which will then override it.
+ *
+ * On non-embedded, framer() has long been exposed as part
+ * of the public KPI, and therefore its signature must
+ * remain the same (without the pre- and postpend length
+ * parameters.) We special case ether_frameout, such that
+ * it gets mapped to its extended variant. All other cases
+ * utilize the stub routine which will simply return zeroes
+ * for those new parameters.
+ *
+ * Internally, DLIL will only use the extended callback
+ * variant which is represented by if_framer.
+ */
+ if (ifp->if_framer == NULL && ifp->if_framer_legacy != NULL) {
+ if (ifp->if_framer_legacy == ether_frameout)
+ ifp->if_framer = ether_frameout_extended;
+ else
+ ifp->if_framer = ifnet_framer_stub;
+ }
+
if (ifp->if_output_bw.eff_bw > ifp->if_output_bw.max_bw)
ifp->if_output_bw.max_bw = ifp->if_output_bw.eff_bw;
else if (ifp->if_output_bw.eff_bw == 0)
else if (ifp->if_input_bw.max_bw == 0)
ifp->if_input_bw = ifp->if_output_bw;
- if (ifp->if_ioctl == NULL)
- ifp->if_ioctl = ifp_if_ioctl;
-
/* Pin if_baudrate to 32 bits */
br = MAX(ifp->if_output_bw.max_bw, ifp->if_input_bw.max_bw);
if (br != 0)
ifp->if_baudrate = (br > 0xFFFFFFFF) ? 0xFFFFFFFF : br;
+ if (ifp->if_output_lt.eff_lt > ifp->if_output_lt.max_lt)
+ ifp->if_output_lt.max_lt = ifp->if_output_lt.eff_lt;
+ else if (ifp->if_output_lt.eff_lt == 0)
+ ifp->if_output_lt.eff_lt = ifp->if_output_lt.max_lt;
+
+ if (ifp->if_input_lt.eff_lt > ifp->if_input_lt.max_lt)
+ ifp->if_input_lt.max_lt = ifp->if_input_lt.eff_lt;
+ else if (ifp->if_input_lt.eff_lt == 0)
+ ifp->if_input_lt.eff_lt = ifp->if_input_lt.max_lt;
+
+ if (ifp->if_output_lt.max_lt == 0)
+ ifp->if_output_lt = ifp->if_input_lt;
+ else if (ifp->if_input_lt.max_lt == 0)
+ ifp->if_input_lt = ifp->if_output_lt;
+
+ if (ifp->if_ioctl == NULL)
+ ifp->if_ioctl = ifp_if_ioctl;
+
if (ifp->if_start != NULL) {
ifp->if_eflags |= IFEF_TXSTART;
if (ifp->if_pre_enqueue == NULL)
else
ifp->if_eflags &= ~IFEF_RXPOLL;
+ ifp->if_output_handler = dlil_output_handler;
+ ifp->if_input_handler = dlil_input_handler;
+
VERIFY(!(einit.flags & IFNET_INIT_LEGACY) ||
(ifp->if_pre_enqueue == NULL && ifp->if_start == NULL &&
ifp->if_output_ctl == NULL && ifp->if_input_poll == NULL &&
bzero(&ifp->if_broadcast, sizeof (ifp->if_broadcast));
}
+ /*
+ * output target queue delay is specified in millisecond
+ * convert it to nanoseconds
+ */
+ IFCQ_TARGET_QDELAY(&ifp->if_snd) =
+ einit.output_target_qdelay * 1000 * 1000;
IFCQ_MAXLEN(&ifp->if_snd) = einit.sndq_maxlen;
+ if (einit.start_delay_qlen > 0 &&
+ einit.start_delay_timeout > 0) {
+ ifp->if_eflags |= IFEF_ENQUEUE_MULTI;
+ ifp->if_start_delay_qlen =
+ min(100, einit.start_delay_qlen);
+ ifp->if_start_delay_timeout =
+ min(20000, einit.start_delay_timeout);
+ /* convert timeout to nanoseconds */
+ ifp->if_start_delay_timeout *= 1000;
+ }
+
if (error == 0) {
*interface = ifp;
// temporary - this should be done in dlil_if_acquire
return ((interface == NULL) ? 0 : interface->if_family);
}
+ifnet_subfamily_t
+ifnet_subfamily(ifnet_t interface)
+{
+ return ((interface == NULL) ? 0 : interface->if_subfamily);
+}
+
u_int32_t
ifnet_unit(ifnet_t interface)
{
* to clear one or more of the associated flags in IFEF_AWDL_MASK,
* return failure.
*
+ * If IFEF_AWDL_RESTRICTED is set by the caller, make sure IFEF_AWDL is set
+ * on the interface.
+ *
* All other flags not associated with AWDL are not affected.
*
* See <net/if.h> for current definition of IFEF_AWDL_MASK.
if (ifp->if_eflags & IFEF_AWDL) {
if (eflags & IFEF_AWDL) {
if ((eflags & IFEF_AWDL_MASK) != IFEF_AWDL_MASK)
- return (1);
+ return (EINVAL);
} else {
*new_eflags &= ~IFEF_AWDL_MASK;
*mask |= IFEF_AWDL_MASK;
} else if (eflags & IFEF_AWDL) {
*new_eflags |= IFEF_AWDL_MASK;
*mask |= IFEF_AWDL_MASK;
- }
+ } else if (eflags & IFEF_AWDL_RESTRICTED &&
+ !(ifp->if_eflags & IFEF_AWDL))
+ return (EINVAL);
return (0);
}
errno_t
ifnet_set_eflags(ifnet_t interface, u_int32_t new_flags, u_int32_t mask)
{
+ uint32_t oeflags;
+ struct kev_msg ev_msg;
+ struct net_event_data ev_data;
+
if (interface == NULL)
return (EINVAL);
+ bzero(&ev_msg, sizeof(ev_msg));
ifnet_lock_exclusive(interface);
/*
* Sanity checks for IFEF_AWDL and its related flags.
ifnet_lock_done(interface);
return (EINVAL);
}
+ oeflags = interface->if_eflags;
interface->if_eflags =
(new_flags & mask) | (interface->if_eflags & ~mask);
ifnet_lock_done(interface);
+ if (interface->if_eflags & IFEF_AWDL_RESTRICTED &&
+ !(oeflags & IFEF_AWDL_RESTRICTED)) {
+ ev_msg.event_code = KEV_DL_AWDL_RESTRICTED;
+ /*
+ * The interface is now restricted to applications that have
+ * the entitlement.
+ * The check for the entitlement will be done in the data
+ * path, so we don't have to do anything here.
+ */
+ } else if (oeflags & IFEF_AWDL_RESTRICTED &&
+ !(interface->if_eflags & IFEF_AWDL_RESTRICTED))
+ ev_msg.event_code = KEV_DL_AWDL_UNRESTRICTED;
+ /*
+ * Notify configd so that it has a chance to perform better
+ * reachability detection.
+ */
+ if (ev_msg.event_code) {
+ bzero(&ev_data, sizeof(ev_data));
+ 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, interface->if_name, IFNAMSIZ);
+ ev_data.if_family = interface->if_family;
+ ev_data.if_unit = interface->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(interface, &ev_msg);
+ }
return (0);
}
if ((after - before) < 0 && ifp->if_idle_flags == 0 &&
ifp->if_want_aggressive_drain != 0) {
ifp->if_want_aggressive_drain = 0;
- if (ifnet_aggressive_drainers == 0)
- panic("%s: ifp=%p negative aggdrain!", __func__, ifp);
- if (--ifnet_aggressive_drainers == 0)
- rt_aggdrain(0);
} else if ((after - before) > 0 && ifp->if_want_aggressive_drain == 0) {
ifp->if_want_aggressive_drain++;
- if (++ifnet_aggressive_drainers == 0)
- panic("%s: ifp=%p wraparound aggdrain!", __func__, ifp);
- else if (ifnet_aggressive_drainers == 1)
- rt_aggdrain(1);
}
return (0);
goto done;
}
- if_lqm_update(ifp, quality);
+ if_lqm_update(ifp, quality, 0);
done:
return (err);
return (IFNET_LQM_THRESH_OFF);
ifnet_lock_shared(ifp);
- lqm = ifp->if_lqm;
+ lqm = ifp->if_interface_state.lqm_state;
ifnet_lock_done(ifp);
return (lqm);
}
+errno_t
+ifnet_set_interface_state(ifnet_t ifp,
+ struct if_interface_state *if_interface_state)
+{
+ errno_t err = 0;
+
+ if (ifp == NULL || if_interface_state == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+
+ if (!ifnet_is_attached(ifp, 0)) {
+ err = ENXIO;
+ goto done;
+ }
+
+ if_state_update(ifp, if_interface_state);
+
+done:
+ return (err);
+}
+
+errno_t
+ifnet_get_interface_state(ifnet_t ifp,
+ struct if_interface_state *if_interface_state)
+{
+ errno_t err = 0;
+
+ if (ifp == NULL || if_interface_state == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+
+ if (!ifnet_is_attached(ifp, 0)) {
+ err = ENXIO;
+ goto done;
+ }
+
+ if_get_state(ifp, if_interface_state);
+
+done:
+ return (err);
+}
+
+
static errno_t
ifnet_defrouter_llreachinfo(ifnet_t ifp, int af,
struct ifnet_llreach_info *iflri)
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;
- kev_post_msg(&ev_msg);
+ dlil_post_complete_msg(ifp, &ev_msg);
return (error);
}
static const ifnet_offload_t offload_mask =
(IFNET_CSUM_IP | IFNET_CSUM_TCP | IFNET_CSUM_UDP | IFNET_CSUM_FRAGMENT |
IFNET_IP_FRAGMENT | IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6 |
- IFNET_IPV6_FRAGMENT | IFNET_CSUM_SUM16 | IFNET_VLAN_TAGGING |
- IFNET_VLAN_MTU | IFNET_MULTIPAGES | IFNET_TSO_IPV4 | IFNET_TSO_IPV6);
+ IFNET_IPV6_FRAGMENT | IFNET_CSUM_PARTIAL | IFNET_VLAN_TAGGING |
+ IFNET_VLAN_MTU | IFNET_MULTIPAGES | IFNET_TSO_IPV4 | IFNET_TSO_IPV6 |
+ IFNET_TX_STATUS | IFNET_HW_TIMESTAMP | IFNET_SW_TIMESTAMP);
-static const ifnet_offload_t any_offload_csum =
- (IFNET_CSUM_IP | IFNET_CSUM_TCP | IFNET_CSUM_UDP | IFNET_CSUM_FRAGMENT |
- IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6 | IFNET_CSUM_SUM16);
+static const ifnet_offload_t any_offload_csum = IFNET_CHECKSUMF;
errno_t
ifnet_set_offload(ifnet_t interface, ifnet_offload_t offload)
ifnet_lock_exclusive(interface);
interface->if_hwassist = (offload & offload_mask);
+ /*
+ * Hardware capable of partial checksum offload is
+ * flexible enough to handle any transports utilizing
+ * Internet Checksumming. Include those transports
+ * here, and leave the final decision to IP.
+ */
+ if (interface->if_hwassist & IFNET_CSUM_PARTIAL) {
+ interface->if_hwassist |= (IFNET_CSUM_TCP | IFNET_CSUM_UDP |
+ IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6);
+ }
+ if (dlil_verbose) {
+ log(LOG_DEBUG, "%s: set offload flags=%b\n",
+ if_name(interface),
+ interface->if_hwassist, IFNET_OFFLOADF_BITS);
+ }
ifnet_lock_done(interface);
if ((offload & any_offload_csum))
ifcaps |= IFCAP_VLAN_MTU;
if ((offload & IFNET_VLAN_TAGGING))
ifcaps |= IFCAP_VLAN_HWTAGGING;
+ if ((offload & IFNET_TX_STATUS))
+ ifcaps |= IFCAP_TXSTATUS;
+ if ((offload & IFNET_HW_TIMESTAMP))
+ ifcaps |= IFCAP_HW_TIMESTAMP;
+ if ((offload & IFNET_SW_TIMESTAMP))
+ ifcaps |= IFCAP_SW_TIMESTAMP;
if (ifcaps != 0) {
(void) ifnet_set_capabilities_supported(interface, ifcaps,
IFCAP_VALID);
ifnet_lock_exclusive(interface);
- interface->if_wake_properties =
- (properties & mask) | (interface->if_wake_properties & ~mask);
+ if (mask & IF_WAKE_ON_MAGIC_PACKET) {
+ if (properties & IF_WAKE_ON_MAGIC_PACKET)
+ interface->if_xflags |= IFXF_WAKE_ON_MAGIC_PACKET;
+ else
+ interface->if_xflags &= ~IFXF_WAKE_ON_MAGIC_PACKET;
+ }
ifnet_lock_done(interface);
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;
- kev_post_msg(&ev_msg);
+ dlil_post_complete_msg(interface, &ev_msg);
return (0);
}
u_int32_t
ifnet_get_wake_flags(ifnet_t interface)
{
- return ((interface == NULL) ? 0 : interface->if_wake_properties);
+ u_int32_t flags = 0;
+
+ if (interface == NULL)
+ return (0);
+
+ if (interface->if_xflags & IFXF_WAKE_ON_MAGIC_PACKET)
+ flags |= IF_WAKE_ON_MAGIC_PACKET;
+
+ return (flags);
}
/*
if (ifp == NULL)
return (EINVAL);
+ /* set input values first (if any), as output values depend on them */
if (input_bw != NULL)
(void) ifnet_set_input_bandwidths(ifp, input_bw);
return (0);
}
+static void
+ifnet_set_link_status_outbw(struct ifnet *ifp)
+{
+ struct if_wifi_status_v1 *sr;
+ sr = &ifp->if_link_status->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+ if (ifp->if_output_bw.eff_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
+ sr->ul_effective_bandwidth =
+ ifp->if_output_bw.eff_bw;
+ }
+ if (ifp->if_output_bw.max_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_UL_MAX_BANDWIDTH_VALID;
+ sr->ul_max_bandwidth =
+ ifp->if_output_bw.max_bw;
+ }
+}
+
errno_t
ifnet_set_output_bandwidths(struct ifnet *ifp, struct if_bandwidths *bw,
boolean_t locked)
struct ifclassq *ifq;
u_int64_t br;
+ VERIFY(ifp != NULL && bw != NULL);
+
ifq = &ifp->if_snd;
if (!locked)
IFCQ_LOCK(ifq);
IFCQ_LOCK_ASSERT_HELD(ifq);
old_bw = ifp->if_output_bw;
- if (bw != NULL) {
- if (bw->eff_bw != 0)
- ifp->if_output_bw.eff_bw = bw->eff_bw;
- if (bw->max_bw != 0)
- ifp->if_output_bw.max_bw = bw->max_bw;
- if (ifp->if_output_bw.eff_bw > ifp->if_output_bw.max_bw)
- ifp->if_output_bw.max_bw = ifp->if_output_bw.eff_bw;
- else if (ifp->if_output_bw.eff_bw == 0)
- ifp->if_output_bw.eff_bw = ifp->if_output_bw.max_bw;
- }
+ if (bw->eff_bw != 0)
+ ifp->if_output_bw.eff_bw = bw->eff_bw;
+ if (bw->max_bw != 0)
+ ifp->if_output_bw.max_bw = bw->max_bw;
+ if (ifp->if_output_bw.eff_bw > ifp->if_output_bw.max_bw)
+ ifp->if_output_bw.max_bw = ifp->if_output_bw.eff_bw;
+ else if (ifp->if_output_bw.eff_bw == 0)
+ ifp->if_output_bw.eff_bw = ifp->if_output_bw.max_bw;
/* Pin if_baudrate to 32 bits */
br = MAX(ifp->if_output_bw.max_bw, ifp->if_input_bw.max_bw);
/* Adjust queue parameters if needed */
if (old_bw.eff_bw != ifp->if_output_bw.eff_bw ||
old_bw.max_bw != ifp->if_output_bw.max_bw)
- ifnet_update_sndq(ifq, CLASSQ_EV_LINK_SPEED);
+ ifnet_update_sndq(ifq, CLASSQ_EV_LINK_BANDWIDTH);
if (!locked)
IFCQ_UNLOCK(ifq);
+ /*
+ * If this is a Wifi interface, update the values in
+ * if_link_status structure also.
+ */
+ if (IFNET_IS_WIFI(ifp) && ifp->if_link_status != NULL) {
+ lck_rw_lock_exclusive(&ifp->if_link_status_lock);
+ ifnet_set_link_status_outbw(ifp);
+ lck_rw_done(&ifp->if_link_status_lock);
+ }
+
return (0);
}
+static void
+ifnet_set_link_status_inbw(struct ifnet *ifp)
+{
+ struct if_wifi_status_v1 *sr;
+
+ sr = &ifp->if_link_status->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+ if (ifp->if_input_bw.eff_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
+ sr->dl_effective_bandwidth =
+ ifp->if_input_bw.eff_bw;
+ }
+ if (ifp->if_input_bw.max_bw != 0) {
+ sr->valid_bitmask |=
+ IF_WIFI_DL_MAX_BANDWIDTH_VALID;
+ sr->dl_max_bandwidth = ifp->if_input_bw.max_bw;
+ }
+}
+
errno_t
ifnet_set_input_bandwidths(struct ifnet *ifp, struct if_bandwidths *bw)
{
struct if_bandwidths old_bw;
+ VERIFY(ifp != NULL && bw != NULL);
+
old_bw = ifp->if_input_bw;
if (bw->eff_bw != 0)
ifp->if_input_bw.eff_bw = bw->eff_bw;
else if (ifp->if_input_bw.eff_bw == 0)
ifp->if_input_bw.eff_bw = ifp->if_input_bw.max_bw;
+ if (IFNET_IS_WIFI(ifp) && ifp->if_link_status != NULL) {
+ lck_rw_lock_exclusive(&ifp->if_link_status_lock);
+ ifnet_set_link_status_inbw(ifp);
+ lck_rw_done(&ifp->if_link_status_lock);
+ }
+
if (old_bw.eff_bw != ifp->if_input_bw.eff_bw ||
old_bw.max_bw != ifp->if_input_bw.max_bw)
- ifnet_update_rcv(ifp, CLASSQ_EV_LINK_SPEED);
+ ifnet_update_rcv(ifp, CLASSQ_EV_LINK_BANDWIDTH);
return (0);
}
return (0);
}
+errno_t
+ifnet_set_latencies(struct ifnet *ifp, struct if_latencies *output_lt,
+ struct if_latencies *input_lt)
+{
+ if (ifp == NULL)
+ return (EINVAL);
+
+ if (output_lt != NULL)
+ (void) ifnet_set_output_latencies(ifp, output_lt, FALSE);
+
+ if (input_lt != NULL)
+ (void) ifnet_set_input_latencies(ifp, input_lt);
+
+ return (0);
+}
+
+errno_t
+ifnet_set_output_latencies(struct ifnet *ifp, struct if_latencies *lt,
+ boolean_t locked)
+{
+ struct if_latencies old_lt;
+ struct ifclassq *ifq;
+
+ VERIFY(ifp != NULL && lt != NULL);
+
+ ifq = &ifp->if_snd;
+ if (!locked)
+ IFCQ_LOCK(ifq);
+ IFCQ_LOCK_ASSERT_HELD(ifq);
+
+ old_lt = ifp->if_output_lt;
+ if (lt->eff_lt != 0)
+ ifp->if_output_lt.eff_lt = lt->eff_lt;
+ if (lt->max_lt != 0)
+ ifp->if_output_lt.max_lt = lt->max_lt;
+ if (ifp->if_output_lt.eff_lt > ifp->if_output_lt.max_lt)
+ ifp->if_output_lt.max_lt = ifp->if_output_lt.eff_lt;
+ else if (ifp->if_output_lt.eff_lt == 0)
+ ifp->if_output_lt.eff_lt = ifp->if_output_lt.max_lt;
+
+ /* Adjust queue parameters if needed */
+ if (old_lt.eff_lt != ifp->if_output_lt.eff_lt ||
+ old_lt.max_lt != ifp->if_output_lt.max_lt)
+ ifnet_update_sndq(ifq, CLASSQ_EV_LINK_LATENCY);
+
+ if (!locked)
+ IFCQ_UNLOCK(ifq);
+
+ return (0);
+}
+
+errno_t
+ifnet_set_input_latencies(struct ifnet *ifp, struct if_latencies *lt)
+{
+ struct if_latencies old_lt;
+
+ VERIFY(ifp != NULL && lt != NULL);
+
+ old_lt = ifp->if_input_lt;
+ if (lt->eff_lt != 0)
+ ifp->if_input_lt.eff_lt = lt->eff_lt;
+ if (lt->max_lt != 0)
+ ifp->if_input_lt.max_lt = lt->max_lt;
+ if (ifp->if_input_lt.eff_lt > ifp->if_input_lt.max_lt)
+ ifp->if_input_lt.max_lt = ifp->if_input_lt.eff_lt;
+ else if (ifp->if_input_lt.eff_lt == 0)
+ ifp->if_input_lt.eff_lt = ifp->if_input_lt.max_lt;
+
+ if (old_lt.eff_lt != ifp->if_input_lt.eff_lt ||
+ old_lt.max_lt != ifp->if_input_lt.max_lt)
+ ifnet_update_rcv(ifp, CLASSQ_EV_LINK_LATENCY);
+
+ return (0);
+}
+
+errno_t
+ifnet_latencies(struct ifnet *ifp, struct if_latencies *output_lt,
+ struct if_latencies *input_lt)
+{
+ if (ifp == NULL)
+ return (EINVAL);
+
+ if (output_lt != NULL)
+ *output_lt = ifp->if_output_lt;
+ if (input_lt != NULL)
+ *input_lt = ifp->if_input_lt;
+
+ return (0);
+}
+
+errno_t
+ifnet_set_poll_params(struct ifnet *ifp, struct ifnet_poll_params *p)
+{
+ errno_t err;
+
+ if (ifp == NULL)
+ return (EINVAL);
+ else if (!ifnet_is_attached(ifp, 1))
+ return (ENXIO);
+
+ err = dlil_rxpoll_set_params(ifp, p, FALSE);
+
+ /* Release the io ref count */
+ ifnet_decr_iorefcnt(ifp);
+
+ return (err);
+}
+
+errno_t
+ifnet_poll_params(struct ifnet *ifp, struct ifnet_poll_params *p)
+{
+ errno_t err;
+
+ if (ifp == NULL || p == NULL)
+ return (EINVAL);
+ else if (!ifnet_is_attached(ifp, 1))
+ return (ENXIO);
+
+ err = dlil_rxpoll_get_params(ifp, p);
+
+ /* Release the io ref count */
+ ifnet_decr_iorefcnt(ifp);
+
+ return (err);
+}
+
errno_t
ifnet_stat_increment(struct ifnet *ifp,
const struct ifnet_stat_increment_param *s)
return (EINVAL);
*last_change = interface->if_data.ifi_lastchange;
-#if IF_LASTCHANGEUPTIME
/* Crude conversion from uptime to calendar time */
last_change->tv_sec += boottime_sec();
-#endif
+
return (0);
}
sa_family_t family)
{
return (ifnet_get_address_list_family_internal(interface, addresses,
- family, 0, M_NOWAIT));
+ family, 0, M_NOWAIT, 0));
+}
+
+errno_t
+ifnet_get_inuse_address_list(ifnet_t interface, ifaddr_t **addresses)
+{
+ return (addresses == NULL ? EINVAL :
+ ifnet_get_address_list_family_internal(interface, addresses,
+ 0, 0, M_NOWAIT, 1));
}
+extern uint32_t tcp_find_anypcb_byaddr(struct ifaddr *ifa);
+
+extern uint32_t udp_find_anypcb_byaddr(struct ifaddr *ifa);
+
__private_extern__ errno_t
ifnet_get_address_list_family_internal(ifnet_t interface, ifaddr_t **addresses,
- sa_family_t family, int detached, int how)
+ sa_family_t family, int detached, int how, int return_inuse_addrs)
{
SLIST_HEAD(, ifnet_addr_list) ifal_head;
struct ifnet_addr_list *ifal, *ifal_tmp;
struct ifnet *ifp;
int count = 0;
errno_t err = 0;
+ int usecount = 0;
+ int index = 0;
SLIST_INIT(&ifal_head);
done:
SLIST_FOREACH_SAFE(ifal, &ifal_head, ifal_le, ifal_tmp) {
SLIST_REMOVE(&ifal_head, ifal, ifnet_addr_list, ifal_le);
- if (err == 0)
- (*addresses)[--count] = ifal->ifal_ifa;
- else
+ if (err == 0) {
+ if (return_inuse_addrs) {
+ usecount = tcp_find_anypcb_byaddr(ifal->ifal_ifa);
+ usecount += udp_find_anypcb_byaddr(ifal->ifal_ifa);
+ if (usecount) {
+ (*addresses)[index] = ifal->ifal_ifa;
+ index++;
+ } else {
+ IFA_REMREF(ifal->ifal_ifa);
+ }
+ } else {
+ (*addresses)[--count] = ifal->ifal_ifa;
+ }
+ } else {
IFA_REMREF(ifal->ifal_ifa);
+ }
FREE(ifal, M_TEMP);
}
+ VERIFY(err == 0 || *addresses == NULL);
+ if ((err == 0) && (count) && ((*addresses)[0] == NULL)) {
+ VERIFY(return_inuse_addrs == 1);
+ FREE(*addresses, M_TEMP);
+ err = ENXIO;
+ }
return (err);
}
/*
* if_lladdr points to the permanent link address of
- * the interface; it never gets deallocated.
+ * the interface and it never gets deallocated; internal
+ * code should simply use IF_LLADDR() for performance.
*/
ifa = interface->if_lladdr;
IFA_LOCK_SPIN(ifa);
return (0);
}
-errno_t
-ifnet_lladdr_copy_bytes(ifnet_t interface, void *lladdr, size_t lladdr_len)
+static errno_t
+ifnet_lladdr_copy_bytes_internal(ifnet_t interface, void *lladdr,
+ size_t lladdr_len, kauth_cred_t *credp)
{
- struct sockaddr_dl *sdl;
+ const u_int8_t *bytes;
+ size_t bytes_len;
struct ifaddr *ifa;
+ uint8_t sdlbuf[SOCK_MAXADDRLEN + 1];
+ errno_t error = 0;
+
+ /*
+ * Make sure to accomodate the largest possible
+ * size of SA(if_lladdr)->sa_len.
+ */
+ _CASSERT(sizeof (sdlbuf) == (SOCK_MAXADDRLEN + 1));
if (interface == NULL || lladdr == NULL)
return (EINVAL);
- /*
- * if_lladdr points to the permanent link address of
- * the interface; it never gets deallocated.
- */
ifa = interface->if_lladdr;
IFA_LOCK_SPIN(ifa);
- sdl = SDL((void *)ifa->ifa_addr);
- if (lladdr_len != sdl->sdl_alen) {
+ bcopy(ifa->ifa_addr, &sdlbuf, SDL(ifa->ifa_addr)->sdl_len);
+ IFA_UNLOCK(ifa);
+
+ bytes = dlil_ifaddr_bytes(SDL(&sdlbuf), &bytes_len, credp);
+ if (bytes_len != lladdr_len) {
bzero(lladdr, lladdr_len);
- IFA_UNLOCK(ifa);
- return (EMSGSIZE);
+ error = EMSGSIZE;
+ } else {
+ bcopy(bytes, lladdr, bytes_len);
}
- bcopy(LLADDR(sdl), lladdr, lladdr_len);
- IFA_UNLOCK(ifa);
- return (0);
+ return (error);
+}
+
+errno_t
+ifnet_lladdr_copy_bytes(ifnet_t interface, void *lladdr, size_t length)
+{
+ return (ifnet_lladdr_copy_bytes_internal(interface, lladdr, length,
+ NULL));
+}
+
+errno_t
+ifnet_guarded_lladdr_copy_bytes(ifnet_t interface, void *lladdr, size_t length)
+{
+#if CONFIG_MACF
+ kauth_cred_t cred;
+ net_thread_marks_t marks;
+#endif
+ kauth_cred_t *credp;
+ errno_t error;
+
+ credp = NULL;
+#if CONFIG_MACF
+ marks = net_thread_marks_push(NET_THREAD_CKREQ_LLADDR);
+ cred = kauth_cred_proc_ref(current_proc());
+ credp = &cred;
+#else
+ credp = NULL;
+#endif
+
+ error = ifnet_lladdr_copy_bytes_internal(interface, lladdr, length,
+ credp);
+
+#if CONFIG_MACF
+ kauth_cred_unref(credp);
+ net_thread_marks_pop(marks);
+#endif
+
+ return (error);
}
static errno_t
void
ifnet_transmit_burst_start(ifnet_t ifp, mbuf_t pkt)
{
+#if MEASURE_BW
uint32_t orig_flags;
if (ifp == NULL || !(pkt->m_flags & M_PKTHDR))
return;
}
- ifp->if_bw.start_seq = pkt->m_pkthdr.pf_mtag.pftag_pktseq;
+ ifp->if_bw.start_seq = pkt->m_pkthdr.pkt_bwseq;
ifp->if_bw.start_ts = mach_absolute_time();
+#else /* !MEASURE_BW */
+#pragma unused(ifp, pkt)
+#endif /* !MEASURE_BW */
}
void
ifnet_transmit_burst_end(ifnet_t ifp, mbuf_t pkt)
{
+#if MEASURE_BW
uint64_t oseq, ots, bytes, ts, t;
uint32_t flags;
- if ( ifp == NULL || !(pkt->m_flags & M_PKTHDR))
+ if (ifp == NULL || !(pkt->m_flags & M_PKTHDR))
return;
flags = OSBitOrAtomic(IF_MEASURED_BW_CALCULATION, &ifp->if_bw.flags);
goto done;
}
- oseq = pkt->m_pkthdr.pf_mtag.pftag_pktseq;
+ oseq = pkt->m_pkthdr.pkt_bwseq;
ots = mach_absolute_time();
if (ifp->if_bw.start_seq > 0 && oseq > ifp->if_bw.start_seq) {
ts = ots - ifp->if_bw.start_ts;
- if (ts > 0 ) {
+ if (ts > 0) {
absolutetime_to_nanoseconds(ts, &t);
bytes = oseq - ifp->if_bw.start_seq;
ifp->if_bw.bytes = bytes;
done:
flags = ~(IF_MEASURED_BW_INPROGRESS | IF_MEASURED_BW_CALCULATION);
OSBitAndAtomic(flags, &ifp->if_bw.flags);
+#else /* !MEASURE_BW */
+#pragma unused(ifp, pkt)
+#endif /* !MEASURE_BW */
}
-/****************************************************************************/
-/* ifaddr_t accessors */
-/****************************************************************************/
+/*************************************************************************/
+/* ifaddr_t accessors */
+/*************************************************************************/
errno_t
ifaddr_reference(ifaddr_t ifa)
return ((ifma == NULL) ? NULL : ifma->ifma_ifp);
}
-/******************************************************************************/
-/* interface cloner */
-/******************************************************************************/
+/**************************************************************************/
+/* interface cloner */
+/**************************************************************************/
errno_t
ifnet_clone_attach(struct ifnet_clone_params *cloner_params,
return (error);
}
-/******************************************************************************/
-/* misc */
-/******************************************************************************/
-
-extern void udp_get_ports_used(unsigned int ifindex, uint8_t *bitfield);
-extern void tcp_get_ports_used(unsigned int ifindex, uint8_t *bitfield);
+/**************************************************************************/
+/* misc */
+/**************************************************************************/
errno_t
-ifnet_get_local_ports(ifnet_t ifp, uint8_t *bitfield)
+ifnet_get_local_ports_extended(ifnet_t ifp, protocol_family_t protocol,
+ u_int32_t flags, u_int8_t *bitfield)
{
+ u_int32_t ifindex;
+ u_int32_t inp_flags = 0;
+
+ inp_flags |= ((flags & IFNET_GET_LOCAL_PORTS_WILDCARDOK) ?
+ INPCB_GET_PORTS_USED_WILDCARDOK : 0);
+ inp_flags |= ((flags & IFNET_GET_LOCAL_PORTS_NOWAKEUPOK) ?
+ INPCB_GET_PORTS_USED_NOWAKEUPOK : 0);
+ inp_flags |= ((flags & IFNET_GET_LOCAL_PORTS_RECVANYIFONLY) ?
+ INPCB_GET_PORTS_USED_RECVANYIFONLY : 0);
+ inp_flags |= ((flags & IFNET_GET_LOCAL_PORTS_EXTBGIDLEONLY) ?
+ INPCB_GET_PORTS_USED_EXTBGIDLEONLY : 0);
+ inp_flags |= ((flags & IFNET_GET_LOCAL_PORTS_ACTIVEONLY) ?
+ INPCB_GET_PORTS_USED_ACTIVEONLY : 0);
+
if (bitfield == NULL)
return (EINVAL);
- bzero(bitfield, 8192);
+ switch (protocol) {
+ case PF_UNSPEC:
+ case PF_INET:
+ case PF_INET6:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* bit string is long enough to hold 16-bit port values */
+ bzero(bitfield, bitstr_size(65536));
+
+ ifindex = (ifp != NULL) ? ifp->if_index : 0;
- udp_get_ports_used(ifp ? ifp->if_index : 0, bitfield);
- tcp_get_ports_used(ifp ? ifp->if_index : 0, bitfield);
+ if (!(flags & IFNET_GET_LOCAL_PORTS_TCPONLY))
+ udp_get_ports_used(ifindex, protocol, inp_flags, bitfield);
+
+ if (!(flags & IFNET_GET_LOCAL_PORTS_UDPONLY))
+ tcp_get_ports_used(ifindex, protocol, inp_flags, bitfield);
return (0);
}
errno_t
-ifnet_notice_node_presence(ifnet_t ifp, struct sockaddr* sa, int32_t rssi,
+ifnet_get_local_ports(ifnet_t ifp, u_int8_t *bitfield)
+{
+ u_int32_t flags = IFNET_GET_LOCAL_PORTS_WILDCARDOK;
+ return (ifnet_get_local_ports_extended(ifp, PF_UNSPEC, flags,
+ bitfield));
+}
+
+errno_t
+ifnet_notice_node_presence(ifnet_t ifp, struct sockaddr *sa, int32_t rssi,
int lqm, int npm, u_int8_t srvinfo[48])
{
if (ifp == NULL || sa == NULL || srvinfo == NULL)
- return(EINVAL);
+ return (EINVAL);
if (sa->sa_len > sizeof(struct sockaddr_storage))
- return(EINVAL);
+ return (EINVAL);
if (sa->sa_family != AF_LINK && sa->sa_family != AF_INET6)
- return(EINVAL);
-
+ return (EINVAL);
+
dlil_node_present(ifp, sa, rssi, lqm, npm, srvinfo);
return (0);
}
errno_t
-ifnet_notice_node_absence(ifnet_t ifp, struct sockaddr* sa)
+ifnet_notice_node_absence(ifnet_t ifp, struct sockaddr *sa)
{
if (ifp == NULL || sa == NULL)
- return(EINVAL);
+ return (EINVAL);
if (sa->sa_len > sizeof(struct sockaddr_storage))
- return(EINVAL);
+ return (EINVAL);
if (sa->sa_family != AF_LINK && sa->sa_family != AF_INET6)
- return(EINVAL);
-
+ return (EINVAL);
+
dlil_node_absent(ifp, sa);
return (0);
}
ifnet_notice_master_elected(ifnet_t ifp)
{
if (ifp == NULL)
- return(EINVAL);
-
+ return (EINVAL);
+
dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_MASTER_ELECTED, NULL, 0);
return (0);
}
+
+errno_t
+ifnet_tx_compl_status(ifnet_t ifp, mbuf_t m, tx_compl_val_t val)
+{
+#pragma unused(val)
+
+ m_do_tx_compl_callback(m, ifp);
+
+ return (0);
+}
+
+errno_t
+ifnet_tx_compl(ifnet_t ifp, mbuf_t m)
+{
+ m_do_tx_compl_callback(m, ifp);
+
+ return (0);
+}
+
+errno_t
+ifnet_report_issues(ifnet_t ifp, u_int8_t modid[IFNET_MODIDLEN],
+ u_int8_t info[IFNET_MODARGLEN])
+{
+ if (ifp == NULL || modid == NULL)
+ return (EINVAL);
+
+ dlil_report_issues(ifp, modid, info);
+ return (0);
+}
+
+errno_t
+ifnet_set_delegate(ifnet_t ifp, ifnet_t delegated_ifp)
+{
+ ifnet_t odifp = NULL;
+
+ if (ifp == NULL)
+ return (EINVAL);
+ else if (!ifnet_is_attached(ifp, 1))
+ return (ENXIO);
+
+ ifnet_lock_exclusive(ifp);
+ odifp = ifp->if_delegated.ifp;
+ if (odifp != NULL && odifp == delegated_ifp) {
+ /* delegate info is unchanged; nothing more to do */
+ ifnet_lock_done(ifp);
+ goto done;
+ }
+ // Test if this delegate interface would cause a loop
+ ifnet_t delegate_check_ifp = delegated_ifp;
+ while (delegate_check_ifp != NULL) {
+ if (delegate_check_ifp == ifp) {
+ printf("%s: delegating to %s would cause a loop\n",
+ ifp->if_xname, delegated_ifp->if_xname);
+ ifnet_lock_done(ifp);
+ goto done;
+ }
+ delegate_check_ifp = delegate_check_ifp->if_delegated.ifp;
+ }
+ bzero(&ifp->if_delegated, sizeof (ifp->if_delegated));
+ if (delegated_ifp != NULL && ifp != delegated_ifp) {
+ ifp->if_delegated.ifp = delegated_ifp;
+ ifnet_reference(delegated_ifp);
+ ifp->if_delegated.type = delegated_ifp->if_type;
+ ifp->if_delegated.family = delegated_ifp->if_family;
+ ifp->if_delegated.subfamily = delegated_ifp->if_subfamily;
+ ifp->if_delegated.expensive =
+ delegated_ifp->if_eflags & IFEF_EXPENSIVE ? 1 : 0;
+
+ /*
+ * Propogate flags related to ECN from delegated interface
+ */
+ ifp->if_eflags &= ~(IFEF_ECN_ENABLE|IFEF_ECN_DISABLE);
+ ifp->if_eflags |= (delegated_ifp->if_eflags &
+ (IFEF_ECN_ENABLE|IFEF_ECN_DISABLE));
+
+ printf("%s: is now delegating %s (type 0x%x, family %u, "
+ "sub-family %u)\n", ifp->if_xname, delegated_ifp->if_xname,
+ delegated_ifp->if_type, delegated_ifp->if_family,
+ delegated_ifp->if_subfamily);
+ }
+
+ ifnet_lock_done(ifp);
+
+ if (odifp != NULL) {
+ if (odifp != delegated_ifp) {
+ printf("%s: is no longer delegating %s\n",
+ ifp->if_xname, odifp->if_xname);
+ }
+ ifnet_release(odifp);
+ }
+
+ /* Generate a kernel event */
+ dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_IFDELEGATE_CHANGED, NULL, 0);
+
+done:
+ /* Release the io ref count */
+ ifnet_decr_iorefcnt(ifp);
+
+ return (0);
+}
+
+errno_t
+ifnet_get_delegate(ifnet_t ifp, ifnet_t *pdelegated_ifp)
+{
+ if (ifp == NULL || pdelegated_ifp == NULL)
+ return (EINVAL);
+ else if (!ifnet_is_attached(ifp, 1))
+ return (ENXIO);
+
+ ifnet_lock_shared(ifp);
+ if (ifp->if_delegated.ifp != NULL)
+ ifnet_reference(ifp->if_delegated.ifp);
+ *pdelegated_ifp = ifp->if_delegated.ifp;
+ ifnet_lock_done(ifp);
+
+ /* Release the io ref count */
+ ifnet_decr_iorefcnt(ifp);
+
+ return (0);
+}
+
+errno_t
+ifnet_get_keepalive_offload_frames(ifnet_t ifp,
+ struct ifnet_keepalive_offload_frame *frames_array,
+ u_int32_t frames_array_count, size_t frame_data_offset,
+ u_int32_t *used_frames_count)
+{
+ u_int32_t i;
+
+ if (frames_array == NULL || used_frames_count == NULL ||
+ frame_data_offset >= IFNET_KEEPALIVE_OFFLOAD_FRAME_DATA_SIZE)
+ return (EINVAL);
+
+ /* frame_data_offset should be 32-bit aligned */
+ if (P2ROUNDUP(frame_data_offset, sizeof(u_int32_t)) !=
+ frame_data_offset)
+ return (EINVAL);
+
+ *used_frames_count = 0;
+ if (frames_array_count == 0)
+ return (0);
+
+ for (i = 0; i < frames_array_count; i++) {
+ struct ifnet_keepalive_offload_frame *frame = frames_array + i;
+
+ bzero(frame, sizeof(struct ifnet_keepalive_offload_frame));
+ }
+
+ /* First collect IPSec related keep-alive frames */
+ *used_frames_count = key_fill_offload_frames_for_savs(ifp,
+ frames_array, frames_array_count, frame_data_offset);
+
+ /* If there is more room, collect other UDP keep-alive frames */
+ if (*used_frames_count < frames_array_count)
+ udp_fill_keepalive_offload_frames(ifp, frames_array,
+ frames_array_count, frame_data_offset,
+ used_frames_count);
+
+ /* If there is more room, collect other TCP keep-alive frames */
+ if (*used_frames_count < frames_array_count)
+ tcp_fill_keepalive_offload_frames(ifp, frames_array,
+ frames_array_count, frame_data_offset,
+ used_frames_count);
+
+ VERIFY(*used_frames_count <= frames_array_count);
+
+ return (0);
+}
+
+errno_t
+ifnet_link_status_report(ifnet_t ifp, const void *buffer,
+ size_t buffer_len)
+{
+ struct if_link_status *ifsr;
+ errno_t err = 0;
+
+ if (ifp == NULL || buffer == NULL || buffer_len == 0)
+ return (EINVAL);
+
+ ifnet_lock_shared(ifp);
+
+ /*
+ * Make sure that the interface is attached but there is no need
+ * to take a reference because this call is coming from the driver.
+ */
+ if (!ifnet_is_attached(ifp, 0)) {
+ ifnet_lock_done(ifp);
+ return (ENXIO);
+ }
+
+ lck_rw_lock_exclusive(&ifp->if_link_status_lock);
+
+ /*
+ * If this is the first status report then allocate memory
+ * to store it.
+ */
+ if (ifp->if_link_status == NULL) {
+ MALLOC(ifp->if_link_status, struct if_link_status *,
+ sizeof(struct if_link_status), M_TEMP, M_ZERO);
+ if (ifp->if_link_status == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ }
+
+ ifsr = __DECONST(struct if_link_status *, buffer);
+
+ if (ifp->if_type == IFT_CELLULAR) {
+ struct if_cellular_status_v1 *if_cell_sr, *new_cell_sr;
+ /*
+ * Currently we have a single version -- if it does
+ * not match, just return.
+ */
+ if (ifsr->ifsr_version !=
+ IF_CELLULAR_STATUS_REPORT_CURRENT_VERSION) {
+ err = ENOTSUP;
+ goto done;
+ }
+
+ if (ifsr->ifsr_len != sizeof(*if_cell_sr)) {
+ err = EINVAL;
+ goto done;
+ }
+
+ if_cell_sr =
+ &ifp->if_link_status->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
+ new_cell_sr = &ifsr->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
+ /* Check if we need to act on any new notifications */
+ if ((new_cell_sr->valid_bitmask &
+ IF_CELL_UL_MSS_RECOMMENDED_VALID) &&
+ new_cell_sr->mss_recommended !=
+ if_cell_sr->mss_recommended) {
+ atomic_bitset_32(&tcbinfo.ipi_flags,
+ INPCBINFO_UPDATE_MSS);
+ inpcb_timer_sched(&tcbinfo, INPCB_TIMER_FAST);
+ }
+
+ /* Finally copy the new information */
+ ifp->if_link_status->ifsr_version = ifsr->ifsr_version;
+ ifp->if_link_status->ifsr_len = ifsr->ifsr_len;
+ if_cell_sr->valid_bitmask = 0;
+ bcopy(new_cell_sr, if_cell_sr, sizeof(*if_cell_sr));
+
+ } else if (ifp->if_subfamily == IFNET_SUBFAMILY_WIFI) {
+ struct if_wifi_status_v1 *if_wifi_sr, *new_wifi_sr;
+
+ /* Check version */
+ if (ifsr->ifsr_version !=
+ IF_WIFI_STATUS_REPORT_CURRENT_VERSION) {
+ err = ENOTSUP;
+ goto done;
+ }
+
+ if (ifsr->ifsr_len != sizeof(*if_wifi_sr)) {
+ err = EINVAL;
+ goto done;
+ }
+
+ if_wifi_sr =
+ &ifp->if_link_status->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+ new_wifi_sr =
+ &ifsr->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+ ifp->if_link_status->ifsr_version = ifsr->ifsr_version;
+ ifp->if_link_status->ifsr_len = ifsr->ifsr_len;
+ if_wifi_sr->valid_bitmask = 0;
+ bcopy(new_wifi_sr, if_wifi_sr, sizeof(*if_wifi_sr));
+
+ /*
+ * Update the bandwidth values if we got recent values
+ * reported through the other KPI.
+ */
+ if (!(new_wifi_sr->valid_bitmask &
+ IF_WIFI_UL_MAX_BANDWIDTH_VALID) &&
+ ifp->if_output_bw.max_bw > 0) {
+ if_wifi_sr->valid_bitmask |=
+ IF_WIFI_UL_MAX_BANDWIDTH_VALID;
+ if_wifi_sr->ul_max_bandwidth =
+ ifp->if_output_bw.max_bw;
+ }
+ if (!(new_wifi_sr->valid_bitmask &
+ IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID) &&
+ ifp->if_output_bw.eff_bw > 0) {
+ if_wifi_sr->valid_bitmask |=
+ IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
+ if_wifi_sr->ul_effective_bandwidth =
+ ifp->if_output_bw.eff_bw;
+ }
+ if (!(new_wifi_sr->valid_bitmask &
+ IF_WIFI_DL_MAX_BANDWIDTH_VALID) &&
+ ifp->if_input_bw.max_bw > 0) {
+ if_wifi_sr->valid_bitmask |=
+ IF_WIFI_DL_MAX_BANDWIDTH_VALID;
+ if_wifi_sr->dl_max_bandwidth =
+ ifp->if_input_bw.max_bw;
+ }
+ if (!(new_wifi_sr->valid_bitmask &
+ IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID) &&
+ ifp->if_input_bw.eff_bw > 0) {
+ if_wifi_sr->valid_bitmask |=
+ IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
+ if_wifi_sr->dl_effective_bandwidth =
+ ifp->if_input_bw.eff_bw;
+ }
+ }
+
+done:
+ lck_rw_done(&ifp->if_link_status_lock);
+ ifnet_lock_done(ifp);
+ return (err);
+}
+
+/*************************************************************************/
+/* Packet preamble */
+/*************************************************************************/
+
+#define MAX_IF_PACKET_PREAMBLE_LEN 32
+
+errno_t
+ifnet_set_packetpreamblelen(ifnet_t interface, u_int32_t len)
+{
+ errno_t err = 0;
+
+ if (interface == NULL || len > MAX_IF_PACKET_PREAMBLE_LEN) {
+ err = EINVAL;
+ goto done;
+ }
+ interface->if_data.ifi_preamblelen = len;
+done:
+ return (err);
+}
+
+u_int32_t
+ifnet_packetpreamblelen(ifnet_t interface)
+{
+ return ((interface == NULL) ? 0 : interface->if_data.ifi_preamblelen);
+}
+
+u_int32_t
+ifnet_maxpacketpreamblelen(void)
+{
+ return (MAX_IF_PACKET_PREAMBLE_LEN);
+}
+
+
+/*************************************************************************/
+/* Fastlane QoS Ca */
+/*************************************************************************/
+
+errno_t
+ifnet_set_fastlane_capable(ifnet_t interface, boolean_t capable)
+{
+ if (interface == NULL)
+ return (EINVAL);
+
+ if_set_qosmarking_mode(interface,
+ capable ? IFRTYPE_QOSMARKING_FASTLANE : IFRTYPE_QOSMARKING_MODE_NONE);
+
+ return (0);
+}
+
+errno_t
+ifnet_get_fastlane_capable(ifnet_t interface, boolean_t *capable)
+{
+ if (interface == NULL || capable == NULL)
+ return (EINVAL);
+ if (interface->if_eflags & IFEF_QOSMARKING_CAPABLE)
+ *capable = true;
+ else
+ *capable = false;
+ return (0);
+}
+
+errno_t
+ifnet_get_unsent_bytes(ifnet_t interface, int64_t *unsent_bytes)
+{
+ int64_t bytes;
+
+ if (interface == NULL || unsent_bytes == NULL)
+ return (EINVAL);
+
+ bytes = *unsent_bytes = 0;
+
+ if ((interface->if_refflags & (IFRF_ATTACHED | IFRF_DETACHING)) !=
+ IFRF_ATTACHED)
+ return (ENXIO);
+
+ bytes = interface->if_sndbyte_unsent;
+
+ if (interface->if_eflags & IFEF_TXSTART)
+ bytes += IFCQ_BYTES(&interface->if_snd);
+ *unsent_bytes = bytes;
+
+ return (0);
+}
+
+errno_t
+ifnet_get_buffer_status(const ifnet_t ifp, ifnet_buffer_status_t *buf_status)
+{
+ if (ifp == NULL || buf_status == NULL)
+ return (EINVAL);
+
+ bzero(buf_status, sizeof (*buf_status));
+
+ if ((ifp->if_refflags & (IFRF_ATTACHED | IFRF_DETACHING)) !=
+ IFRF_ATTACHED)
+ return (ENXIO);
+
+ buf_status->buf_sndbuf = ifp->if_sndbyte_unsent;
+
+ if (ifp->if_eflags & IFEF_TXSTART)
+ buf_status->buf_interface = IFCQ_BYTES(&ifp->if_snd);
+
+ return (0);
+}
+
+void
+ifnet_normalise_unsent_data(void)
+{
+ struct ifnet *ifp;
+
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
+ ifnet_lock_exclusive(ifp);
+ if ((ifp->if_refflags & (IFRF_ATTACHED|IFRF_DETACHING)) !=
+ IFRF_ATTACHED) {
+ ifnet_lock_done(ifp);
+ continue;
+ }
+ if (!(ifp->if_eflags & IFEF_TXSTART)) {
+ ifnet_lock_done(ifp);
+ continue;
+ }
+
+ if (ifp->if_sndbyte_total > 0 ||
+ IFCQ_BYTES(&ifp->if_snd) > 0)
+ ifp->if_unsent_data_cnt++;
+
+ ifnet_lock_done(ifp);
+ }
+ ifnet_head_done();
+}