mirror of
https://github.com/Telecominfraproject/wlan-cloud-rrm.git
synced 2025-10-29 09:42:22 +00:00
Client steering notes, docs, fixes (#128)
Signed-off-by: Jeffrey Han <39203126+elludraon@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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<String, Map<String, String>> 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.<iface> bss_mgmt_enable \
|
||||
// '{"neighbor_report": true, "beacon_report": true, "link_measurements": true, "bss_transition": true}'
|
||||
//
|
||||
// Actions:
|
||||
//
|
||||
// - Kick/Deauth:
|
||||
// ubus call hostapd.<iface> del_client \
|
||||
// '{"addr": "<client_mac>", "reason": 5, "deauth": true}'
|
||||
// Where "reason" is a code in BTMReasonCode
|
||||
//
|
||||
// - Steer:
|
||||
// ubus call hostapd.<iface> bss_transition_request \
|
||||
// '{"addr": "<client_mac>", "disassociation_imminent": false, "disassociation_timer": 0, "validity_period": 30, "neighbors": ["<hex>"], "abridged": 1}'
|
||||
// Where "neighbors" list element = a hex identifier (array index 2 in command below) - MUST fetch per interface per AP
|
||||
// ubus call hostapd.<iface> 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": "<client_mac>", "channel": <number>, "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.<iface>
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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<? extends State> 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,
|
||||
|
||||
Reference in New Issue
Block a user