[MSG_ASSOC] = { "assoc", BLOBMSG_TYPE_BOOL },
};
struct blob_attr *tb[__MSG_MAX];
+ struct usteer_remote_node *rn;
+ struct sta_info *remote_si;
blobmsg_parse(policy, __MSG_MAX, tb, blobmsg_data(data), blobmsg_data_len(data));
- if (tb[MSG_ASSOC] && blobmsg_get_u8(tb[MSG_ASSOC]))
+ if (tb[MSG_ASSOC] && blobmsg_get_u8(tb[MSG_ASSOC])) {
+ if (si->connected == STA_NOT_CONNECTED) {
+ /* New connection. Check if STA roamed. */
+ for_each_remote_node(rn) {
+ remote_si = usteer_sta_info_get(si->sta, &rn->node, NULL);
+ if (!remote_si)
+ continue;
+
+ if (current_time - remote_si->last_connected < config.roam_process_timeout) {
+ rn->node.roam_source++;
+ /* Don't abort looking for roam sources here.
+ * The client might have roamed via another node
+ * within the roam-timeout.
+ */
+ }
+ }
+ }
si->connected = STA_CONNECTED;
+ }
if (si->node->freq < 4000)
si->sta->seen_2ghz = 1;
config.remote_node_timeout = 10;
config.roam_kick_delay = 100;
+ config.roam_process_timeout = 5 * 1000;
config.roam_scan_tries = 3;
config.roam_scan_interval = 10 * 1000;
config.roam_trigger_interval = 60 * 1000;
# Minimum signal-to-noise ratio or signal level (dBm) to remain connected
#option min_snr 0
+ # Timeout (in ms) after which a association following a disassociation is not seen
+ # as a roam
+ #option roam_process_timeout 5000
+
# Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger
# client scans for roaming
#option roam_scan_snr 0
load_balancing_threshold band_steering_threshold \
remote_update_interval \
min_connect_snr min_snr signal_diff_threshold \
- initial_connect_delay \
+ initial_connect_delay roam_process_timeout\
roam_kick_delay roam_scan_tries \
roam_scan_snr roam_scan_interval \
roam_trigger_snr roam_trigger_interval \
interface_add_station(struct usteer_remote_node *node, struct blob_attr *data)
{
struct sta *sta;
- struct sta_info *si;
+ struct sta_info *si, *local_si;
struct apmsg_sta msg;
+ struct usteer_node *local_node;
bool create;
+ bool connect_change;
if (!parse_apmsg_sta(&msg, data)) {
MSG(DEBUG, "Cannot parse station in message\n");
if (!si)
return;
+ connect_change = si->connected != msg.connected;
si->connected = msg.connected;
si->signal = msg.signal;
si->seen = current_time - msg.seen;
si->last_connected = current_time - msg.last_connected;
+
+ /* Check if client roamed to this foreign node */
+ if ((connect_change || create) && si->connected == STA_CONNECTED) {
+ for_each_local_node(local_node) {
+ local_si = usteer_sta_info_get(sta, local_node, NULL);
+ if (!local_si)
+ continue;
+
+ if (current_time - local_si->last_connected < config.roam_process_timeout) {
+ node->node.roam_destination++;
+ break;
+ }
+ }
+ }
+
usteer_sta_info_update_timeout(si, msg.timeout);
}
_cfg(BOOL, assoc_steering), \
_cfg(I32, min_connect_snr), \
_cfg(I32, min_snr), \
+ _cfg(U32, roam_process_timeout), \
_cfg(I32, roam_scan_snr), \
_cfg(U32, roam_scan_tries), \
_cfg(U32, roam_scan_interval), \
blobmsg_add_u32(buf, "noise", node->noise);
blobmsg_add_u32(buf, "load", node->load);
blobmsg_add_u32(buf, "max_assoc", node->max_assoc);
+ blobmsg_add_u32(buf, "roam_source", node->roam_source);
+ blobmsg_add_u32(buf, "roam_destination", node->roam_destination);
if (node->rrm_nr)
blobmsg_add_field(buf, BLOBMSG_TYPE_ARRAY, "rrm_nr",
blobmsg_data(node->rrm_nr),
int n_assoc;
int max_assoc;
int load;
+
+ int roam_source;
+ int roam_destination;
};
struct usteer_scan_request {
uint32_t signal_diff_threshold;
int32_t roam_scan_snr;
+ uint32_t roam_process_timeout;
+
uint32_t roam_scan_tries;
uint32_t roam_scan_interval;