mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 17:59:25 +01:00
device: Add support for AP directed roaming
This patch adds a watcher/parser for the frame event associated with an AP directed BSS transition (AP roaming). When the AP sends a BSS transition request, this will parse out the BSS candidate list (neighbor report) and initiate a roam scan. After this point the existing roaming code path is reused.
This commit is contained in:
parent
6a9de526a8
commit
38e9a67da8
148
src/device.c
148
src/device.c
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* Wireless daemon for Linux
|
* Wireless daemon for Linux
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013-2016 Intel Corporation. All rights reserved.
|
* Copyright (C) 2013-2018 Intel Corporation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -90,6 +90,9 @@ struct device {
|
|||||||
bool preparing_roam : 1;
|
bool preparing_roam : 1;
|
||||||
bool signal_low : 1;
|
bool signal_low : 1;
|
||||||
bool roam_no_orig_ap : 1;
|
bool roam_no_orig_ap : 1;
|
||||||
|
bool ap_directed_roaming : 1;
|
||||||
|
|
||||||
|
uint32_t ap_roam_watch;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct signal_agent {
|
struct signal_agent {
|
||||||
@ -763,6 +766,7 @@ static void device_roam_failed(struct device *device)
|
|||||||
|
|
||||||
device->preparing_roam = false;
|
device->preparing_roam = false;
|
||||||
device->roam_no_orig_ap = false;
|
device->roam_no_orig_ap = false;
|
||||||
|
device->ap_directed_roaming = false;
|
||||||
|
|
||||||
if (device->state == DEVICE_STATE_ROAMING)
|
if (device->state == DEVICE_STATE_ROAMING)
|
||||||
device_disassociated(device);
|
device_disassociated(device);
|
||||||
@ -921,6 +925,9 @@ static void device_transition_start(struct device *device, struct scan_bss *bss)
|
|||||||
l_debug("%d, target %s", device->index,
|
l_debug("%d, target %s", device->index,
|
||||||
util_address_to_string(bss->addr));
|
util_address_to_string(bss->addr));
|
||||||
|
|
||||||
|
/* Reset AP roam flag, at this point the roaming behaves the same */
|
||||||
|
device->ap_directed_roaming = false;
|
||||||
|
|
||||||
if (hs->mde)
|
if (hs->mde)
|
||||||
ie_parse_mobility_domain_from_data(hs->mde, hs->mde[1] + 2,
|
ie_parse_mobility_domain_from_data(hs->mde, hs->mde[1] + 2,
|
||||||
&mdid, NULL, NULL);
|
&mdid, NULL, NULL);
|
||||||
@ -1054,6 +1061,11 @@ static bool device_roam_scan_notify(uint32_t wiphy_id, uint32_t ifindex,
|
|||||||
struct ie_rsn_info info;
|
struct ie_rsn_info info;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
/* Skip the BSS we are connected to if doing an AP roam */
|
||||||
|
if (device->ap_directed_roaming && !memcmp(bss->addr,
|
||||||
|
device->connected_bss->addr, 6))
|
||||||
|
goto next;
|
||||||
|
|
||||||
/* Skip result if it is not part of the ESS */
|
/* Skip result if it is not part of the ESS */
|
||||||
|
|
||||||
if (bss->ssid_len != hs->ssid_len ||
|
if (bss->ssid_len != hs->ssid_len ||
|
||||||
@ -1153,6 +1165,29 @@ static void device_roam_scan(struct device *device,
|
|||||||
device_roam_failed(device);
|
device_roam_failed(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t device_freq_from_neighbor_report(const uint8_t *country,
|
||||||
|
struct ie_neighbor_report_info *info)
|
||||||
|
{
|
||||||
|
enum scan_band band;
|
||||||
|
uint32_t freq;
|
||||||
|
|
||||||
|
band = scan_oper_class_to_band(country, info->oper_class);
|
||||||
|
if (!band) {
|
||||||
|
l_debug("Ignored: unsupported oper class");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
freq = scan_channel_to_freq(info->channel_num, band);
|
||||||
|
if (!freq) {
|
||||||
|
l_debug("Ignored: unsupported channel");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
static void device_neighbor_report_cb(struct netdev *netdev, int err,
|
static void device_neighbor_report_cb(struct netdev *netdev, int err,
|
||||||
const uint8_t *reports,
|
const uint8_t *reports,
|
||||||
size_t reports_len, void *user_data)
|
size_t reports_len, void *user_data)
|
||||||
@ -1186,7 +1221,6 @@ static void device_neighbor_report_cb(struct netdev *netdev, int err,
|
|||||||
/* First see if any of the reports contain the MD bit set */
|
/* First see if any of the reports contain the MD bit set */
|
||||||
while (ie_tlv_iter_next(&iter)) {
|
while (ie_tlv_iter_next(&iter)) {
|
||||||
struct ie_neighbor_report_info info;
|
struct ie_neighbor_report_info info;
|
||||||
enum scan_band band;
|
|
||||||
uint32_t freq;
|
uint32_t freq;
|
||||||
const uint8_t *cc = NULL;
|
const uint8_t *cc = NULL;
|
||||||
|
|
||||||
@ -1205,19 +1239,9 @@ static void device_neighbor_report_cb(struct netdev *netdev, int err,
|
|||||||
if (device->connected_bss->cc_present)
|
if (device->connected_bss->cc_present)
|
||||||
cc = device->connected_bss->cc;
|
cc = device->connected_bss->cc;
|
||||||
|
|
||||||
band = scan_oper_class_to_band(cc, info.oper_class);
|
freq = device_freq_from_neighbor_report(cc, &info);
|
||||||
if (!band) {
|
if (!freq)
|
||||||
l_debug("Ignored: unsupported oper class");
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
freq = scan_channel_to_freq(info.channel_num, band);
|
|
||||||
if (!freq) {
|
|
||||||
l_debug("Ignored: unsupported channel");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!memcmp(info.addr, device->connected_bss->addr, ETH_ALEN)) {
|
if (!memcmp(info.addr, device->connected_bss->addr, ETH_ALEN)) {
|
||||||
/*
|
/*
|
||||||
@ -1333,6 +1357,91 @@ static void device_roam_timeout_rearm(struct device *device, int seconds)
|
|||||||
l_timeout_create(seconds, device_roam_trigger_cb, device, NULL);
|
l_timeout_create(seconds, device_roam_trigger_cb, device, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define AP_ROAM_BSS_LIST_BIT (1 << 0)
|
||||||
|
#define AP_ROAM_BSS_TERMINATION_BIT (1 << 3)
|
||||||
|
#define AP_ROAM_ESS_DISSASSOCIATION_IMMINENT (1 << 4)
|
||||||
|
|
||||||
|
static void device_ap_roam_frame_event(struct netdev *netdev,
|
||||||
|
const struct mmpdu_header *hdr,
|
||||||
|
const void *body, size_t body_len,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct device *device = user_data;
|
||||||
|
uint32_t pos = 0;
|
||||||
|
uint8_t req_mode;
|
||||||
|
uint16_t dtimer;
|
||||||
|
uint8_t valid_interval;
|
||||||
|
|
||||||
|
if (device->preparing_roam || device->state == DEVICE_STATE_ROAMING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (body_len < 7)
|
||||||
|
goto format_error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First two bytes are checked by the frame watch (WNM category and
|
||||||
|
* WNM action). The third is the dialog token which is not relevant
|
||||||
|
* because we did not send a BSS transition query -- so skip these
|
||||||
|
* first three bytes.
|
||||||
|
*/
|
||||||
|
pos += 3;
|
||||||
|
|
||||||
|
req_mode = l_get_u8(body + pos);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Disassociation timer and validity interval are currently not
|
||||||
|
* used since the BSS transition request is being handled immediately.
|
||||||
|
*/
|
||||||
|
dtimer = l_get_le16(body + pos);
|
||||||
|
pos += 2;
|
||||||
|
valid_interval = l_get_u8(body + pos);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
l_debug("BSS transition received from AP: Disassociation Time: %u, "
|
||||||
|
"Validity interval: %u", dtimer, valid_interval);
|
||||||
|
|
||||||
|
/* check req_mode for optional values */
|
||||||
|
if (req_mode & AP_ROAM_BSS_TERMINATION_BIT) {
|
||||||
|
if (pos + 12 > body_len)
|
||||||
|
goto format_error;
|
||||||
|
|
||||||
|
pos += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_mode & AP_ROAM_ESS_DISSASSOCIATION_IMMINENT) {
|
||||||
|
uint8_t url_len;
|
||||||
|
|
||||||
|
if (pos + 1 > body_len)
|
||||||
|
goto format_error;
|
||||||
|
|
||||||
|
url_len = l_get_u8(body + pos);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (pos + url_len > body_len)
|
||||||
|
goto format_error;
|
||||||
|
|
||||||
|
pos += url_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->ap_directed_roaming = true;
|
||||||
|
device->preparing_roam = true;
|
||||||
|
|
||||||
|
l_timeout_remove(device->roam_trigger_timeout);
|
||||||
|
device->roam_trigger_timeout = NULL;
|
||||||
|
|
||||||
|
if (req_mode & AP_ROAM_BSS_LIST_BIT)
|
||||||
|
device_neighbor_report_cb(device->netdev, 0, body + pos,
|
||||||
|
body_len - pos, device);
|
||||||
|
else
|
||||||
|
device_roam_scan(device, NULL);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
format_error:
|
||||||
|
l_debug("bad AP roam frame formatting");
|
||||||
|
}
|
||||||
|
|
||||||
static void device_lost_beacon(struct device *device)
|
static void device_lost_beacon(struct device *device)
|
||||||
{
|
{
|
||||||
l_debug("%d", device->index);
|
l_debug("%d", device->index);
|
||||||
@ -2216,6 +2325,7 @@ struct device *device_create(struct wiphy *wiphy, struct netdev *netdev)
|
|||||||
struct device *device;
|
struct device *device;
|
||||||
struct l_dbus *dbus = dbus_get_bus();
|
struct l_dbus *dbus = dbus_get_bus();
|
||||||
uint32_t ifindex = netdev_get_ifindex(netdev);
|
uint32_t ifindex = netdev_get_ifindex(netdev);
|
||||||
|
const uint8_t action_ap_roam_prefix[2] = { 0x0a, 0x07 };
|
||||||
|
|
||||||
device = l_new(struct device, 1);
|
device = l_new(struct device, 1);
|
||||||
device->bss_list = l_queue_new();
|
device->bss_list = l_queue_new();
|
||||||
@ -2252,6 +2362,13 @@ struct device *device_create(struct wiphy *wiphy, struct netdev *netdev)
|
|||||||
device->netdev_watch_id =
|
device->netdev_watch_id =
|
||||||
netdev_watch_add(netdev, device_netdev_notify, device);
|
netdev_watch_add(netdev, device_netdev_notify, device);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* register for AP roam transition watch
|
||||||
|
*/
|
||||||
|
device->ap_roam_watch = netdev_frame_watch_add(netdev, 0x00d0,
|
||||||
|
action_ap_roam_prefix, sizeof(action_ap_roam_prefix),
|
||||||
|
device_ap_roam_frame_event, device);
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2297,6 +2414,9 @@ static void device_free(void *user)
|
|||||||
l_timeout_remove(device->roam_trigger_timeout);
|
l_timeout_remove(device->roam_trigger_timeout);
|
||||||
|
|
||||||
scan_ifindex_remove(device->index);
|
scan_ifindex_remove(device->index);
|
||||||
|
|
||||||
|
netdev_frame_watch_remove(device->netdev, device->ap_roam_watch);
|
||||||
|
|
||||||
l_free(device);
|
l_free(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user