diff --git a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/IEUtils.java b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/IEUtils.java index 322a678..6da2f20 100644 --- a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/IEUtils.java +++ b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/IEUtils.java @@ -69,6 +69,24 @@ public abstract class IEUtils { return element.getAsInt(); } + /** + * Try to get a json object as a int + * + * @param contents the JSON object to try to parse + * @param fieldName the field name + * @return the field as a int (0 if key not present) + */ + public static Integer parseIntField( + JsonObject contents, + String fieldName + ) { + JsonElement element = contents.get(fieldName); + if (element == null) { + return 0; + } + return element.getAsInt(); + } + /** * Try to get a json object as a string * @@ -86,4 +104,22 @@ public abstract class IEUtils { } return element.getAsString(); } + + /** + * Try to get a json object as a boolean when represented as a number (0, 1) + * + * @param contents the JSON object to try to parse + * @param fieldName the field name + * @return the field as a boolean (false if key not present) + */ + public static boolean parseBooleanNumberField( + JsonObject contents, + String fieldName + ) { + JsonElement element = contents.get(fieldName); + if (element == null) { + return false; + } + return element.getAsInt() > 0; + } } diff --git a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/InformationElements.java b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/InformationElements.java index fd57386..e14b5b9 100644 --- a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/InformationElements.java +++ b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/InformationElements.java @@ -13,38 +13,42 @@ import java.util.Objects; import com.facebook.openwifi.cloudsdk.ies.Country; import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint; import com.facebook.openwifi.cloudsdk.ies.QbssLoad; +import com.facebook.openwifi.cloudsdk.ies.RMEnabledCapabilities; import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo; /** Wrapper class containing information elements */ public final class InformationElements { - public Country country; public QbssLoad qbssLoad; public LocalPowerConstraint localPowerConstraint; public TxPwrInfo txPwrInfo; + public RMEnabledCapabilities rmEnabledCapabilities; @Override public int hashCode() { - return Objects.hash(country, localPowerConstraint, qbssLoad, txPwrInfo); + return Objects.hash( + country, + localPowerConstraint, + qbssLoad, + rmEnabledCapabilities, + txPwrInfo + ); } @Override public boolean equals(Object obj) { - if (this == obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } InformationElements other = (InformationElements) obj; - return Objects.equals(country, other.country) && Objects.equals( - localPowerConstraint, - other.localPowerConstraint - ) && Objects.equals(qbssLoad, other.qbssLoad) && + return Objects.equals(country, other.country) && + Objects.equals(localPowerConstraint, other.localPowerConstraint) && + Objects.equals(qbssLoad, other.qbssLoad) && + Objects + .equals(rmEnabledCapabilities, other.rmEnabledCapabilities) && Objects.equals(txPwrInfo, other.txPwrInfo); } - } diff --git a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/UCentralUtils.java b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/UCentralUtils.java index 8c25fe7..c35ef9b 100644 --- a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/UCentralUtils.java +++ b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/UCentralUtils.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import com.facebook.openwifi.cloudsdk.ies.Country; import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint; import com.facebook.openwifi.cloudsdk.ies.QbssLoad; +import com.facebook.openwifi.cloudsdk.ies.RMEnabledCapabilities; import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo; import com.facebook.openwifi.cloudsdk.models.ap.Capabilities; import com.facebook.openwifi.cloudsdk.models.ap.State; @@ -172,6 +173,10 @@ public class UCentralUtils { case TxPwrInfo.TYPE: ieContainer.txPwrInfo = TxPwrInfo.parse(contents); break; + case RMEnabledCapabilities.TYPE: + ieContainer.rmEnabledCapabilities = + RMEnabledCapabilities.parse(contents); + break; } } catch (Exception e) { logger.error(String.format("Skipping invalid IE %s", ie), e); diff --git a/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/ies/RMEnabledCapabilities.java b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/ies/RMEnabledCapabilities.java new file mode 100644 index 0000000..b5af60f --- /dev/null +++ b/lib-cloudsdk/src/main/java/com/facebook/openwifi/cloudsdk/ies/RMEnabledCapabilities.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.openwifi.cloudsdk.ies; + +import java.util.Objects; + +import com.facebook.openwifi.cloudsdk.IEUtils; +import com.google.gson.JsonObject; + +/** + * This information element (IE) appears in wifiscan entries. It's called "RM + * Enabled Capabilities" in 802.11 specs (section 9.4.2.45). Refer to the + * specification for more details. Language in javadocs is taken from the + * specification. + */ +public class RMEnabledCapabilities { + /** Defined in 802.11 table 9-92 */ + public static final int TYPE = 70; + + // Bit fields + // @formatter:off + public final boolean linkMeasurementCapabilityEnabled; + public final boolean neighborReportCapabilityEnabled; + public final boolean parallelMeasurementsCapabilityEnabled; + public final boolean repeatedMeasurementsCapabilityEnabled; + public final boolean beaconPassiveMeasurementCapabilityEnabled; + public final boolean beaconActiveMeasurementCapabilityEnabled; + public final boolean beaconTableMeasurementCapabilityEnabled; + public final boolean beaconMeasurementReportingConditionsCapabilityEnabled; + public final boolean frameMeasurementCapabilityEnabled; + public final boolean channelLoadMeasurementCapabilityEnabled; + public final boolean noiseHistogramMeasurementCapabilityEnabled; + public final boolean statisticsMeasurementCapabilityEnabled; + public final boolean lciMeasurementCapabilityEnabled; + public final boolean lciAzimuthCapabilityEnabled; + public final boolean transmitStreamCategoryMeasurementCapabilityEnabled; + public final boolean triggeredTransmitStreamCategoryMeasurementCapabilityEnabled; + public final boolean apChannelReportCapabilityEnabled; + public final boolean rmMibCapabilityEnabled; + public final int operatingChannelMaxMeasurementDuration; + public final int nonoperatingChannelMaxMeasurementDuration; + public final int measurementPilotCapability; + public final boolean measurementPilotTransmissionInformationCapabilityEnabled; + public final boolean neighborReportTsfOffsetCapabilityEnabled; + public final boolean rcpiMeasurementCapabilityEnabled; + public final boolean rsniMeasurementCapabilityEnabled; + public final boolean bssAverageAccessDelayCapabilityEnabled; + public final boolean bssAvailableAdmissionCapacityCapabilityEnabled; + public final boolean antennaCapabilityEnabled; + public final boolean ftmRangeReportCapabilityEnabled; + public final boolean civicLocationMeasurementCapabilityEnabled; + // @formatter:on + + /** Constructor */ + public RMEnabledCapabilities( + boolean linkMeasurementCapabilityEnabled, + boolean neighborReportCapabilityEnabled, + boolean parallelMeasurementsCapabilityEnabled, + boolean repeatedMeasurementsCapabilityEnabled, + boolean beaconPassiveMeasurementCapabilityEnabled, + boolean beaconActiveMeasurementCapabilityEnabled, + boolean beaconTableMeasurementCapabilityEnabled, + boolean beaconMeasurementReportingConditionsCapabilityEnabled, + boolean frameMeasurementCapabilityEnabled, + boolean channelLoadMeasurementCapabilityEnabled, + boolean noiseHistogramMeasurementCapabilityEnabled, + boolean statisticsMeasurementCapabilityEnabled, + boolean lciMeasurementCapabilityEnabled, + boolean lciAzimuthCapabilityEnabled, + boolean transmitStreamCategoryMeasurementCapabilityEnabled, + boolean triggeredTransmitStreamCategoryMeasurementCapabilityEnabled, + boolean apChannelReportCapabilityEnabled, + boolean rmMibCapabilityEnabled, + int operatingChannelMaxMeasurementDuration, + int nonoperatingChannelMaxMeasurementDuration, + int measurementPilotCapability, + boolean measurementPilotTransmissionInformationCapabilityEnabled, + boolean neighborReportTsfOffsetCapabilityEnabled, + boolean rcpiMeasurementCapabilityEnabled, + boolean rsniMeasurementCapabilityEnabled, + boolean bssAverageAccessDelayCapabilityEnabled, + boolean bssAvailableAdmissionCapacityCapabilityEnabled, + boolean antennaCapabilityEnabled, + boolean ftmRangeReportCapabilityEnabled, + boolean civicLocationMeasurementCapabilityEnabled + ) { + // @formatter:off + this.linkMeasurementCapabilityEnabled = linkMeasurementCapabilityEnabled; + this.neighborReportCapabilityEnabled = neighborReportCapabilityEnabled; + this.parallelMeasurementsCapabilityEnabled = parallelMeasurementsCapabilityEnabled; + this.repeatedMeasurementsCapabilityEnabled = repeatedMeasurementsCapabilityEnabled; + this.beaconPassiveMeasurementCapabilityEnabled = beaconPassiveMeasurementCapabilityEnabled; + this.beaconActiveMeasurementCapabilityEnabled = beaconActiveMeasurementCapabilityEnabled; + this.beaconTableMeasurementCapabilityEnabled = beaconTableMeasurementCapabilityEnabled; + this.beaconMeasurementReportingConditionsCapabilityEnabled = beaconMeasurementReportingConditionsCapabilityEnabled; + this.frameMeasurementCapabilityEnabled = frameMeasurementCapabilityEnabled; + this.channelLoadMeasurementCapabilityEnabled = channelLoadMeasurementCapabilityEnabled; + this.noiseHistogramMeasurementCapabilityEnabled = noiseHistogramMeasurementCapabilityEnabled; + this.statisticsMeasurementCapabilityEnabled = statisticsMeasurementCapabilityEnabled; + this.lciMeasurementCapabilityEnabled = lciMeasurementCapabilityEnabled; + this.lciAzimuthCapabilityEnabled = lciAzimuthCapabilityEnabled; + this.transmitStreamCategoryMeasurementCapabilityEnabled = transmitStreamCategoryMeasurementCapabilityEnabled; + this.triggeredTransmitStreamCategoryMeasurementCapabilityEnabled = triggeredTransmitStreamCategoryMeasurementCapabilityEnabled; + this.apChannelReportCapabilityEnabled = apChannelReportCapabilityEnabled; + this.rmMibCapabilityEnabled = rmMibCapabilityEnabled; + this.operatingChannelMaxMeasurementDuration = operatingChannelMaxMeasurementDuration; + this.nonoperatingChannelMaxMeasurementDuration = nonoperatingChannelMaxMeasurementDuration; + this.measurementPilotCapability = measurementPilotCapability; + this.measurementPilotTransmissionInformationCapabilityEnabled = measurementPilotTransmissionInformationCapabilityEnabled; + this.neighborReportTsfOffsetCapabilityEnabled = neighborReportTsfOffsetCapabilityEnabled; + this.rcpiMeasurementCapabilityEnabled = rcpiMeasurementCapabilityEnabled; + this.rsniMeasurementCapabilityEnabled = rsniMeasurementCapabilityEnabled; + this.bssAverageAccessDelayCapabilityEnabled = bssAverageAccessDelayCapabilityEnabled; + this.bssAvailableAdmissionCapacityCapabilityEnabled = bssAvailableAdmissionCapacityCapabilityEnabled; + this.antennaCapabilityEnabled = antennaCapabilityEnabled; + this.ftmRangeReportCapabilityEnabled = ftmRangeReportCapabilityEnabled; + this.civicLocationMeasurementCapabilityEnabled = civicLocationMeasurementCapabilityEnabled; + // @formatter:on + } + + /** Parse RMEnabledCapabilities IE from appropriate Json object. */ + public static RMEnabledCapabilities parse(JsonObject contents) { + JsonObject o = contents.get("RM Capabilities").getAsJsonObject(); + // @formatter:off + return new RMEnabledCapabilities( + /* bits 0-17 */ + IEUtils.parseBooleanNumberField(o, "Link Measurement"), + IEUtils.parseBooleanNumberField(o, "Neighbor Report"), + IEUtils.parseBooleanNumberField(o, "Parallel Measurements"), + IEUtils.parseBooleanNumberField(o, "Repeated Measurements"), + IEUtils.parseBooleanNumberField(o, "Beacon Passive Measurement"), + IEUtils.parseBooleanNumberField(o, "Beacon Active Measurement"), + IEUtils.parseBooleanNumberField(o, "Beacon Table Measurement"), + IEUtils.parseBooleanNumberField(o, "Beacon Measurement Reporting Conditions"), + IEUtils.parseBooleanNumberField(o, "Frame Measurement"), + IEUtils.parseBooleanNumberField(o, "Channel Load Measurement"), + IEUtils.parseBooleanNumberField(o, "Noise Histogram Measurement"), + IEUtils.parseBooleanNumberField(o, "Statistics Measurement"), + IEUtils.parseBooleanNumberField(o, "LCI Measurement"), + IEUtils.parseBooleanNumberField(o, "LCI Azimuth capability"), + IEUtils.parseBooleanNumberField(o, "Transmit Stream/Category Measurement"), + IEUtils.parseBooleanNumberField(o, "Triggered Transmit Stream/Category Measurement"), + IEUtils.parseBooleanNumberField(o, "AP Channel Report capability"), + IEUtils.parseBooleanNumberField(o, "RM MIB capability"), + /* bits 18-20 */ + IEUtils.parseIntField(o, "Operating Channel Max Measurement Duration"), + /* bits 21-23 */ + IEUtils.parseIntField(o, "Nonoperating Channel Max Measurement Duration"), + /* bits 24-26 */ + IEUtils.parseIntField(o, "Measurement Pilotcapability"), + /* bits 27-35 */ + false /* TODO "Measurement Pilot Transmission Information Capability" */, + IEUtils.parseBooleanNumberField(o, "Neighbor Report TSF Offset"), + IEUtils.parseBooleanNumberField(o, "RCPI Measurement capability"), + IEUtils.parseBooleanNumberField(o, "RSNI Measurement capability"), + IEUtils.parseBooleanNumberField(o, "BSS Average Access Delay capability"), + IEUtils.parseBooleanNumberField(o, "BSS Available Admission Capacity capability"), + IEUtils.parseBooleanNumberField(o, "Antenna capability"), + false /* TODO "FTM Range Report Capability" */, + false /* TODO "Civic Location Measurement Capability" */ + /* bits 36-39 reserved */ + ); + // @formatter:on + } + + @Override + public int hashCode() { + return Objects.hash( + antennaCapabilityEnabled, + apChannelReportCapabilityEnabled, + beaconActiveMeasurementCapabilityEnabled, + beaconMeasurementReportingConditionsCapabilityEnabled, + beaconPassiveMeasurementCapabilityEnabled, + beaconTableMeasurementCapabilityEnabled, + bssAvailableAdmissionCapacityCapabilityEnabled, + bssAverageAccessDelayCapabilityEnabled, + channelLoadMeasurementCapabilityEnabled, + civicLocationMeasurementCapabilityEnabled, + frameMeasurementCapabilityEnabled, + ftmRangeReportCapabilityEnabled, + lciAzimuthCapabilityEnabled, + lciMeasurementCapabilityEnabled, + linkMeasurementCapabilityEnabled, + measurementPilotCapability, + measurementPilotTransmissionInformationCapabilityEnabled, + neighborReportCapabilityEnabled, + neighborReportTsfOffsetCapabilityEnabled, + noiseHistogramMeasurementCapabilityEnabled, + nonoperatingChannelMaxMeasurementDuration, + operatingChannelMaxMeasurementDuration, + parallelMeasurementsCapabilityEnabled, + rcpiMeasurementCapabilityEnabled, + repeatedMeasurementsCapabilityEnabled, + rmMibCapabilityEnabled, + rsniMeasurementCapabilityEnabled, + statisticsMeasurementCapabilityEnabled, + transmitStreamCategoryMeasurementCapabilityEnabled, + triggeredTransmitStreamCategoryMeasurementCapabilityEnabled + ); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RMEnabledCapabilities other = (RMEnabledCapabilities) obj; + return antennaCapabilityEnabled == other.antennaCapabilityEnabled && + apChannelReportCapabilityEnabled == + other.apChannelReportCapabilityEnabled && + beaconActiveMeasurementCapabilityEnabled == + other.beaconActiveMeasurementCapabilityEnabled && + beaconMeasurementReportingConditionsCapabilityEnabled == + other.beaconMeasurementReportingConditionsCapabilityEnabled && + beaconPassiveMeasurementCapabilityEnabled == + other.beaconPassiveMeasurementCapabilityEnabled && + beaconTableMeasurementCapabilityEnabled == + other.beaconTableMeasurementCapabilityEnabled && + bssAvailableAdmissionCapacityCapabilityEnabled == + other.bssAvailableAdmissionCapacityCapabilityEnabled && + bssAverageAccessDelayCapabilityEnabled == + other.bssAverageAccessDelayCapabilityEnabled && + channelLoadMeasurementCapabilityEnabled == + other.channelLoadMeasurementCapabilityEnabled && + civicLocationMeasurementCapabilityEnabled == + other.civicLocationMeasurementCapabilityEnabled && + frameMeasurementCapabilityEnabled == + other.frameMeasurementCapabilityEnabled && + ftmRangeReportCapabilityEnabled == + other.ftmRangeReportCapabilityEnabled && + lciAzimuthCapabilityEnabled == other.lciAzimuthCapabilityEnabled && + lciMeasurementCapabilityEnabled == + other.lciMeasurementCapabilityEnabled && + linkMeasurementCapabilityEnabled == + other.linkMeasurementCapabilityEnabled && + measurementPilotCapability == other.measurementPilotCapability && + measurementPilotTransmissionInformationCapabilityEnabled == + other.measurementPilotTransmissionInformationCapabilityEnabled && + neighborReportCapabilityEnabled == + other.neighborReportCapabilityEnabled && + neighborReportTsfOffsetCapabilityEnabled == + other.neighborReportTsfOffsetCapabilityEnabled && + noiseHistogramMeasurementCapabilityEnabled == + other.noiseHistogramMeasurementCapabilityEnabled && + nonoperatingChannelMaxMeasurementDuration == + other.nonoperatingChannelMaxMeasurementDuration && + operatingChannelMaxMeasurementDuration == + other.operatingChannelMaxMeasurementDuration && + parallelMeasurementsCapabilityEnabled == + other.parallelMeasurementsCapabilityEnabled && + rcpiMeasurementCapabilityEnabled == + other.rcpiMeasurementCapabilityEnabled && + repeatedMeasurementsCapabilityEnabled == + other.repeatedMeasurementsCapabilityEnabled && + rmMibCapabilityEnabled == other.rmMibCapabilityEnabled && + rsniMeasurementCapabilityEnabled == + other.rsniMeasurementCapabilityEnabled && + statisticsMeasurementCapabilityEnabled == + other.statisticsMeasurementCapabilityEnabled && + transmitStreamCategoryMeasurementCapabilityEnabled == + other.transmitStreamCategoryMeasurementCapabilityEnabled && + triggeredTransmitStreamCategoryMeasurementCapabilityEnabled == + other.triggeredTransmitStreamCategoryMeasurementCapabilityEnabled; + } +} diff --git a/owrrm/ALGORITHMS.md b/owrrm/ALGORITHMS.md index 2b9a9df..ef790b5 100644 --- a/owrrm/ALGORITHMS.md +++ b/owrrm/ALGORITHMS.md @@ -94,6 +94,30 @@ levels of these APs will be determined by the following steps: Parameters: * `mode`: "measure_ap_ap" * `coverageThreshold`: Coverage threshold between APs in dBm - * values: int < 30 (default: -70) + * values: int < 30 (default: -70) * `nthSmallestRssi`: the nth smallest RSSI that is used for tx power calculation * values: int >= 0 (default: 0) + +## Client Steering +`ClientSteeringOptimizer` and its subclasses implement client steering +algorithms via 802.11k/v/r mechanisms, with the goal of moving clients to +optimal APs and/or bands. + +**Client steering is a work in progress and NOT currently functional.** + +### `SingleAPBandSteering` +This algorithm performs same-AP RRSI-based steering only, using a simple +decision and backoff procedure. + +Parameters: +* `mode`: "band" +* `minRssi2G`: RSSI (dBm) below which a client on the 2G band should be kicked + * values: int < 30 (default: -87) +* `maxRssi2G`: RSSI (dBm) above which a client on the 2G band should roam to + 5G/6G + * values: int < 30 (default: -67) +* `minRssiNon2G`: RSSI (dBm) below which a client on the 5G/6G band should roam + to 2G + * values: int < 30 (default: -82) +* `backoffTimeSec`: Backoff time (seconds) for all APs and radios + * values: int >= 0 (default: 300) diff --git a/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringOptimizer.java b/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringOptimizer.java index 25e2374..39ec074 100644 --- a/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringOptimizer.java +++ b/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringOptimizer.java @@ -18,7 +18,7 @@ import com.facebook.openwifi.rrm.modules.Modeler.DataModel; public abstract class ClientSteeringOptimizer { // TODO call upon triggers, not only via one-off or period runs - /** Represents client steering actions an AP Can take */ + /** Represents client steering actions an AP can take */ public static enum CLIENT_STEERING_ACTIONS { /** Steer from 2G to 5G/6G */ STEER_UP, @@ -28,6 +28,57 @@ public abstract class ClientSteeringOptimizer { DEAUTHENTICATE } + /** + * 802.11 BTM reason codes (ex. for deauth). + * + * See IEEE Std 802.11-2016, 9.4.1.7, Table 9-45. + */ + public static class BTMReasonCode { + private BTMReasonCode() {} + + public static final int UNSPECIFIED = 1; + public static final int PREV_AUTH_NOT_VALID = 2; + public static final int DEAUTH_LEAVING = 3; + public static final int DISASSOC_DUE_TO_INACTIVITY = 4; + public static final int DISASSOC_AP_BUSY = 5; + public static final int CLASS2_FRAME_FROM_NONAUTH_STA = 6; + public static final int CLASS3_FRAME_FROM_NONASSOC_STA = 7; + public static final int DISASSOC_STA_HAS_LEFT = 8; + public static final int STA_REQ_ASSOC_WITHOUT_AUTH = 9; + public static final int PWR_CAPABILITY_NOT_VALID = 10; + public static final int SUPPORTED_CHANNEL_NOT_VALID = 11; + public static final int INVALID_IE = 13; + public static final int MICHAEL_MIC_FAILURE = 14; + public static final int FOURWAY_HANDSHAKE_TIMEOUT = 15; + public static final int GROUP_KEY_UPDATE_TIMEOUT = 16; + public static final int IE_IN_4WAY_DIFFERS = 17; + public static final int GROUP_CIPHER_NOT_VALID = 18; + public static final int PAIRWISE_CIPHER_NOT_VALID = 19; + public static final int AKMP_NOT_VALID = 20; + public static final int UNSUPPORTED_RSN_IE_VERSION = 21; + public static final int INVALID_RSN_IE_CAPAB = 22; + public static final int IEEE_802_1X_AUTH_FAILED = 23; + public static final int CIPHER_SUITE_REJECTED = 24; + public static final int TDLS_TEARDOWN_UNREACHABLE = 25; + public static final int TDLS_TEARDOWN_UNSPECIFIED = 26; + public static final int DISASSOC_LOW_ACK = 34; + public static final int MESH_PEERING_CANCELLED = 52; + public static final int MESH_MAX_PEERS = 53; + public static final int MESH_CONFIG_POLICY_VIOLATION = 54; + public static final int MESH_CLOSE_RCVD = 55; + public static final int MESH_MAX_RETRIES = 56; + public static final int MESH_CONFIRM_TIMEOUT = 57; + public static final int MESH_INVALID_GTK = 58; + public static final int MESH_INCONSISTENT_PARAMS = 59; + public static final int MESH_INVALID_SECURITY_CAP = 60; + public static final int MESH_PATH_ERROR_NO_PROXY_INFO = 61; + public static final int MESH_PATH_ERROR_NO_FORWARDING_INFO = 62; + public static final int MESH_PATH_ERROR_DEST_UNREACHABLE = 63; + public static final int MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS = 64; + public static final int MESH_CHANNEL_SWITCH_REGULATORY_REQ = 65; + public static final int MESH_CHANNEL_SWITCH_UNSPECIFIED = 66; + } + /** The input data model. */ protected final DataModel model; /** The RF zone. */ @@ -79,5 +130,32 @@ public abstract class ClientSteeringOptimizer { Map> apClientActionMap ) { // FIXME implement this + // + // TODO: input must also contain AP interface for each client (needed in hostapd commands below) + // + // NOTE: 802.11k/v features must first be enabled on APs: + // ubus call hostapd. bss_mgmt_enable \ + // '{"neighbor_report": true, "beacon_report": true, "link_measurements": true, "bss_transition": true}' + // + // Actions: + // + // - Kick/Deauth: + // ubus call hostapd. del_client \ + // '{"addr": "", "reason": 5, "deauth": true}' + // Where "reason" is a code in BTMReasonCode + // + // - Steer: + // ubus call hostapd. bss_transition_request \ + // '{"addr": "", "disassociation_imminent": false, "disassociation_timer": 0, "validity_period": 30, "neighbors": [""], "abridged": 1}' + // Where "neighbors" list element = a hex identifier (array index 2 in command below) - MUST fetch per interface per AP + // ubus call hostapd. rrm_nr_get_own + // TODO: also send Multi Band Operation (MBO) code ("mbo_reason") for 802.11ax clients } + + // TODO Issue 802.11k RRM Beacon Measurement Requests periodically + // 1. Enable 802.11k/v features on the AP ("bss_mgmt_enable" hostapd command) + // 2. Send request to client + // ubus call hostapd.wlan0-1 rrm_beacon_req '{"addr": "", "channel": , "mode": 1, "op_class": 128, "duration": 100}' + // 3. Must be subscribed to hostapd 'beacon-report' event on AP to receive reply ("BEACON-RESP-RX") + // ubus subscribe hostapd. } diff --git a/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringState.java b/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringState.java index b6a8e07..a1e344d 100644 --- a/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringState.java +++ b/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/ClientSteeringState.java @@ -15,10 +15,6 @@ import java.util.concurrent.ConcurrentMap; /** Class to manage global client steering state */ public class ClientSteeringState { - - /** Default constructor */ - public ClientSteeringState() {}; - /** * Map from AP serial number to client MAC to time (JVM monotonic time in * ns) of the latest attempted client steering action. The {@code Long} diff --git a/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/SingleAPBandSteering.java b/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/SingleAPBandSteering.java index b12f43f..333602a 100644 --- a/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/SingleAPBandSteering.java +++ b/owrrm/src/main/java/com/facebook/openwifi/rrm/optimizers/clientsteering/SingleAPBandSteering.java @@ -32,16 +32,15 @@ import com.google.gson.Gson; * 6G clients below a configurable RSSI threshold are asked to move to 2G. */ public class SingleAPBandSteering extends ClientSteeringOptimizer { - - /** The Gson instance. */ - private static final Gson gson = new Gson(); - private static final Logger logger = LoggerFactory.getLogger(SingleAPBandSteering.class); /** The RRM algorithm ID. */ public static final String ALGORITHM_ID = "band"; + /** The Gson instance. */ + private static final Gson gson = new Gson(); + /** * RSSI (dBm) below which a client on 2G should be disconnected using * deauthentication. @@ -92,8 +91,8 @@ public class SingleAPBandSteering extends ClientSteeringOptimizer { if ((arg = args.get("minRssiNon2G")) != null) { minRssiNon2G = Short.parseShort(arg); } - if ((arg = args.get("backoffTimeNs")) != null) { - backoffTimeNs = Short.parseShort(arg); + if ((arg = args.get("backoffTimeSec")) != null) { + backoffTimeNs = Long.parseLong(arg) * 1_000_000_000L; } return new SingleAPBandSteering( @@ -139,6 +138,8 @@ public class SingleAPBandSteering extends ClientSteeringOptimizer { // get the latest state // TODO window size (look at multiple states) // TODO window percent (% of samples that must violate thresholds) + // TODO also check wifiscan IEs to see if 11k beacon requests are supported/enabled + // (RMEnabledCapabilities.beaconActiveMeasurementCapabilityEnabled) List states = entry.getValue(); if (states == null || states.isEmpty()) { continue; @@ -207,7 +208,8 @@ public class SingleAPBandSteering extends ClientSteeringOptimizer { * @param serialNumber AP serial number * @param currentTimeNs JVM monotonic time in ns * @param dryRun if set, do not apply changes - * @param apClientActionMap map from AP serial number to client MAC to client steering action name ({@link ClientSteeringOptimizer.CLIENT_STEERING_ACTIONS#name()}) + * @param apClientActionMap map from AP serial number to client MAC to client + * steering action name ({@link ClientSteeringOptimizer.CLIENT_STEERING_ACTIONS}) */ private void maybeAddApClientActionEntry( State.Interface.SSID.Association assoc,