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