Client steering notes, docs, fixes (#128)

Signed-off-by: Jeffrey Han <39203126+elludraon@users.noreply.github.com>
This commit is contained in:
Jeffrey Han
2022-11-14 12:55:43 -08:00
committed by GitHub
parent ea3a13e98c
commit ac5a1c8887
8 changed files with 445 additions and 26 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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>
}

View File

@@ -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}

View File

@@ -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,