X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..eee3565979933af707c711411001ba11fe406a3c:/bsd/net/kpi_interface.c diff --git a/bsd/net/kpi_interface.c b/bsd/net/kpi_interface.c index 22b18df05..d28af82ac 100644 --- a/bsd/net/kpi_interface.c +++ b/bsd/net/kpi_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2014 Apple Inc. All rights reserved. + * Copyright (c) 2004-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -69,6 +69,7 @@ #ifdef INET6 #include #endif +#include #include "net/net_str_id.h" @@ -154,6 +155,7 @@ ifnet_allocate_extended(const struct ifnet_init_eparams *einit0, { struct ifnet_init_eparams einit; struct ifnet *ifp = NULL; + char if_xname[IFXNAMSIZ] = {0}; int error; einit = *einit0; @@ -194,6 +196,14 @@ ifnet_allocate_extended(const struct ifnet_init_eparams *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); @@ -317,6 +327,9 @@ ifnet_allocate_extended(const struct ifnet_init_eparams *einit0, 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 && @@ -346,10 +359,25 @@ ifnet_allocate_extended(const struct ifnet_init_eparams *einit0, 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; + 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 @@ -484,7 +512,7 @@ ifnet_flags(ifnet_t interface) * If IFEF_AWDL has been set on the interface and the caller attempts * 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. * @@ -568,8 +596,8 @@ ifnet_set_eflags(ifnet_t interface, u_int32_t new_flags, u_int32_t mask) 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; - kev_post_msg(&ev_msg); + ev_msg.dv[1].data_length = 0; + dlil_post_complete_msg(interface, &ev_msg); } return (0); @@ -612,12 +640,8 @@ ifnet_set_idle_flags_locked(ifnet_t ifp, u_int32_t new_flags, u_int32_t mask) 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); } 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); } return (0); @@ -658,7 +682,7 @@ ifnet_set_link_quality(ifnet_t ifp, int quality) goto done; } - if_lqm_update(ifp, quality); + if_lqm_update(ifp, quality, 0); done: return (err); @@ -673,12 +697,57 @@ ifnet_link_quality(ifnet_t ifp) 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) @@ -765,7 +834,7 @@ ifnet_set_capabilities_enabled(ifnet_t ifp, u_int32_t new_caps, 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); } @@ -781,11 +850,9 @@ static const ifnet_offload_t offload_mask = IFNET_IP_FRAGMENT | IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6 | IFNET_IPV6_FRAGMENT | IFNET_CSUM_PARTIAL | IFNET_VLAN_TAGGING | IFNET_VLAN_MTU | IFNET_MULTIPAGES | IFNET_TSO_IPV4 | IFNET_TSO_IPV6 | - IFNET_TX_STATUS); + 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_PARTIAL); +static const ifnet_offload_t any_offload_csum = IFNET_CHECKSUMF; errno_t ifnet_set_offload(ifnet_t interface, ifnet_offload_t offload) @@ -824,8 +891,12 @@ ifnet_set_offload(ifnet_t interface, ifnet_offload_t offload) ifcaps |= IFCAP_VLAN_MTU; if ((offload & IFNET_VLAN_TAGGING)) ifcaps |= IFCAP_VLAN_HWTAGGING; - if ((offload & IFNET_TX_STATUS)) - ifcaps |= IFNET_TX_STATUS; + 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); @@ -923,8 +994,12 @@ ifnet_set_wake_flags(ifnet_t interface, u_int32_t properties, u_int32_t mask) 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); @@ -942,7 +1017,7 @@ ifnet_set_wake_flags(ifnet_t interface, u_int32_t properties, u_int32_t mask) 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); } @@ -950,7 +1025,15 @@ ifnet_set_wake_flags(ifnet_t interface, u_int32_t properties, u_int32_t mask) 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); } /* @@ -1129,6 +1212,25 @@ ifnet_set_bandwidths(struct ifnet *ifp, struct if_bandwidths *output_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) @@ -1167,9 +1269,38 @@ ifnet_set_output_bandwidths(struct ifnet *ifp, struct if_bandwidths *bw, 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) { @@ -1187,6 +1318,12 @@ ifnet_set_input_bandwidths(struct ifnet *ifp, struct if_bandwidths *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_BANDWIDTH); @@ -1526,7 +1663,7 @@ 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, + ifnet_get_address_list_family_internal(interface, addresses, 0, 0, M_NOWAIT, 1)); } @@ -1625,17 +1762,16 @@ done: if (return_inuse_addrs) { usecount = tcp_find_anypcb_byaddr(ifal->ifal_ifa); usecount += udp_find_anypcb_byaddr(ifal->ifal_ifa); - if (usecount) { + if (usecount) { (*addresses)[index] = ifal->ifal_ifa; index++; - } - else + } else { IFA_REMREF(ifal->ifal_ifa); + } } else { (*addresses)[--count] = ifal->ifal_ifa; } - } - else { + } else { IFA_REMREF(ifal->ifal_ifa); } FREE(ifal, M_TEMP); @@ -2081,7 +2217,7 @@ ifnet_transmit_burst_start(ifnet_t ifp, mbuf_t pkt) ifp->if_bw.start_seq = pkt->m_pkthdr.pkt_bwseq; ifp->if_bw.start_ts = mach_absolute_time(); -#else /*!MEASURE_BW */ +#else /* !MEASURE_BW */ #pragma unused(ifp, pkt) #endif /* !MEASURE_BW */ } @@ -2093,7 +2229,7 @@ ifnet_transmit_burst_end(ifnet_t ifp, mbuf_t pkt) 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); @@ -2116,7 +2252,7 @@ ifnet_transmit_burst_end(ifnet_t ifp, mbuf_t pkt) 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; @@ -2154,9 +2290,9 @@ done: #endif /* !MEASURE_BW */ } -/****************************************************************************/ -/* ifaddr_t accessors */ -/****************************************************************************/ +/*************************************************************************/ +/* ifaddr_t accessors */ +/*************************************************************************/ errno_t ifaddr_reference(ifaddr_t ifa) @@ -2400,9 +2536,9 @@ ifmaddr_ifnet(ifmultiaddr_t ifma) return ((ifma == NULL) ? NULL : ifma->ifma_ifp); } -/******************************************************************************/ -/* interface cloner */ -/******************************************************************************/ +/**************************************************************************/ +/* interface cloner */ +/**************************************************************************/ errno_t ifnet_clone_attach(struct ifnet_clone_params *cloner_params, @@ -2480,9 +2616,9 @@ fail: return (error); } -/******************************************************************************/ -/* misc */ -/******************************************************************************/ +/**************************************************************************/ +/* misc */ +/**************************************************************************/ errno_t ifnet_get_local_ports_extended(ifnet_t ifp, protocol_family_t protocol, @@ -2495,6 +2631,12 @@ ifnet_get_local_ports_extended(ifnet_t ifp, protocol_family_t protocol, 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); @@ -2526,34 +2668,34 @@ errno_t 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, + 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, +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); @@ -2563,7 +2705,7 @@ errno_t 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); @@ -2572,8 +2714,18 @@ ifnet_notice_master_elected(ifnet_t ifp) errno_t ifnet_tx_compl_status(ifnet_t ifp, mbuf_t m, tx_compl_val_t val) { -#pragma unused(ifp, m, val) - /* Dummy function to be implemented XXX */ +#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); } @@ -2588,7 +2740,7 @@ ifnet_report_issues(ifnet_t ifp, u_int8_t modid[IFNET_MODIDLEN], return (0); } -extern errno_t +errno_t ifnet_set_delegate(ifnet_t ifp, ifnet_t delegated_ifp) { ifnet_t odifp = NULL; @@ -2605,6 +2757,17 @@ ifnet_set_delegate(ifnet_t ifp, ifnet_t delegated_ifp) 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; @@ -2612,13 +2775,22 @@ ifnet_set_delegate(ifnet_t ifp, ifnet_t 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 = + 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) { @@ -2639,7 +2811,7 @@ done: return (0); } -extern errno_t +errno_t ifnet_get_delegate(ifnet_t ifp, ifnet_t *pdelegated_ifp) { if (ifp == NULL || pdelegated_ifp == NULL) @@ -2659,28 +2831,323 @@ ifnet_get_delegate(ifnet_t ifp, ifnet_t *pdelegated_ifp) return (0); } -extern u_int32_t key_fill_offload_frames_for_savs (ifnet_t ifp, - struct ipsec_offload_frame *frames_array, u_int32_t frames_array_count, - size_t frame_data_offset); - -extern errno_t -ifnet_get_ipsec_offload_frames(ifnet_t ifp, - struct ipsec_offload_frame *frames_array, - u_int32_t frames_array_count, - size_t frame_data_offset, - u_int32_t *used_frames_count) +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) { - if (frames_array == NULL || used_frames_count == NULL) { + u_int32_t i; + + if (frames_array == NULL || used_frames_count == NULL || + frame_data_offset >= IFNET_KEEPALIVE_OFFLOAD_FRAME_DATA_SIZE) return (EINVAL); - } - *used_frames_count = 0; + /* frame_data_offset should be 32-bit aligned */ + if (P2ROUNDUP(frame_data_offset, sizeof(u_int32_t)) != + frame_data_offset) + return (EINVAL); - if (frames_array_count == 0) { + *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); + 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(); +}