mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-27 02:32:52 +01:00
ap: Handle most WSC IEs using ap_write_extra_ies
Make the WSC IE processing and writing more self-contained (i.e. so that it can be more easily moved to a separate file if desired) by using the new ap_write_extra_ies() mechanism.
This commit is contained in:
parent
18a63f91fd
commit
f868c2989c
559
src/ap.c
559
src/ap.c
@ -467,6 +467,289 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
||||
rsn->group_cipher = ap->group_cipher;
|
||||
}
|
||||
|
||||
static void ap_wsc_exit_pbc(struct ap_state *ap)
|
||||
{
|
||||
if (!ap->wsc_pbc_timeout)
|
||||
return;
|
||||
|
||||
l_timeout_remove(ap->wsc_pbc_timeout);
|
||||
ap->wsc_dpid = 0;
|
||||
ap_update_beacon(ap);
|
||||
|
||||
ap->ops->handle_event(AP_EVENT_PBC_MODE_EXIT, NULL, ap->user_data);
|
||||
}
|
||||
|
||||
struct ap_pbc_record_expiry_data {
|
||||
uint64_t min_time;
|
||||
const uint8_t *mac;
|
||||
};
|
||||
|
||||
static bool ap_wsc_pbc_record_expire(void *data, void *user_data)
|
||||
{
|
||||
struct ap_wsc_pbc_probe_record *record = data;
|
||||
const struct ap_pbc_record_expiry_data *expiry_data = user_data;
|
||||
|
||||
if (record->timestamp > expiry_data->min_time &&
|
||||
memcmp(record->mac, expiry_data->mac, 6))
|
||||
return false;
|
||||
|
||||
l_free(record);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define AP_WSC_PBC_MONITOR_TIME 120
|
||||
#define AP_WSC_PBC_WALK_TIME 120
|
||||
|
||||
static void ap_process_wsc_probe_req(struct ap_state *ap, const uint8_t *from,
|
||||
const uint8_t *wsc_data,
|
||||
size_t wsc_data_len)
|
||||
{
|
||||
struct wsc_probe_request req;
|
||||
struct ap_pbc_record_expiry_data expiry_data;
|
||||
struct ap_wsc_pbc_probe_record *record;
|
||||
uint64_t now;
|
||||
bool empty;
|
||||
uint8_t first_sta_addr[6] = {};
|
||||
const struct l_queue_entry *entry;
|
||||
|
||||
if (wsc_parse_probe_request(wsc_data, wsc_data_len, &req) < 0)
|
||||
return;
|
||||
|
||||
if (!(req.config_methods & WSC_CONFIGURATION_METHOD_PUSH_BUTTON))
|
||||
return;
|
||||
|
||||
if (req.device_password_id != WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON)
|
||||
return;
|
||||
|
||||
/* Save the address of the first enrollee record */
|
||||
record = l_queue_peek_head(ap->wsc_pbc_probes);
|
||||
if (record)
|
||||
memcpy(first_sta_addr, record->mac, 6);
|
||||
|
||||
now = l_time_now();
|
||||
|
||||
/*
|
||||
* Expire entries older than PBC Monitor Time. While there also drop
|
||||
* older entries from the same Enrollee that sent us this new Probe
|
||||
* Request. It's unclear whether we should also match by the UUID-E.
|
||||
*/
|
||||
expiry_data.min_time = now - AP_WSC_PBC_MONITOR_TIME * 1000000;
|
||||
expiry_data.mac = from;
|
||||
l_queue_foreach_remove(ap->wsc_pbc_probes, ap_wsc_pbc_record_expire,
|
||||
&expiry_data);
|
||||
|
||||
empty = l_queue_isempty(ap->wsc_pbc_probes);
|
||||
|
||||
if (!ap->wsc_pbc_probes)
|
||||
ap->wsc_pbc_probes = l_queue_new();
|
||||
|
||||
/* Add new record */
|
||||
record = l_new(struct ap_wsc_pbc_probe_record, 1);
|
||||
memcpy(record->mac, from, 6);
|
||||
memcpy(record->uuid_e, req.uuid_e, sizeof(record->uuid_e));
|
||||
record->timestamp = now;
|
||||
l_queue_push_tail(ap->wsc_pbc_probes, record);
|
||||
|
||||
/*
|
||||
* If queue was non-empty and we've added one more record then we
|
||||
* now have seen more than one PBC enrollee during the PBC Monitor
|
||||
* Time and must exit "active PBC mode" due to "session overlap".
|
||||
* WSC v2.0.5 Section 11.3:
|
||||
* "Within the PBC Monitor Time, if the Registrar receives PBC
|
||||
* probe requests from more than one Enrollee [...] then the
|
||||
* Registrar SHALL signal a "session overlap" error. As a result,
|
||||
* the Registrar shall refuse to enter active PBC mode and shall
|
||||
* also refuse to perform a PBC-based Registration Protocol
|
||||
* exchange [...]"
|
||||
*/
|
||||
if (empty)
|
||||
return;
|
||||
|
||||
if (ap->wsc_pbc_timeout) {
|
||||
l_debug("Exiting PBC mode due to Session Overlap");
|
||||
ap_wsc_exit_pbc(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* "If the Registrar is engaged in PBC Registration Protocol
|
||||
* exchange with an Enrollee and receives a Probe Request or M1
|
||||
* Message from another Enrollee, then the Registrar should
|
||||
* signal a "session overlap" error".
|
||||
*
|
||||
* For simplicity just interrupt the handshake with that enrollee.
|
||||
*/
|
||||
for (entry = l_queue_get_entries(ap->sta_states); entry;
|
||||
entry = entry->next) {
|
||||
struct sta_state *sta = entry->data;
|
||||
|
||||
if (!sta->associated || sta->assoc_rsne)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check whether this enrollee is in PBC Registration
|
||||
* Protocol by comparing its mac with the first (and only)
|
||||
* record we had in ap->wsc_pbc_probes. If we had more
|
||||
* than one record we wouldn't have been in
|
||||
* "active PBC mode".
|
||||
*/
|
||||
if (memcmp(sta->addr, first_sta_addr, 6) ||
|
||||
!memcmp(sta->addr, from, 6))
|
||||
continue;
|
||||
|
||||
l_debug("Interrupting handshake with %s due to Session Overlap",
|
||||
util_address_to_string(sta->addr));
|
||||
|
||||
if (sta->hs) {
|
||||
netdev_handshake_failed(sta->hs,
|
||||
MMPDU_REASON_CODE_DISASSOC_AP_BUSY);
|
||||
sta->sm = NULL;
|
||||
}
|
||||
|
||||
ap_remove_sta(sta);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t ap_get_wsc_ie_len(struct ap_state *ap,
|
||||
enum mpdu_management_subtype type,
|
||||
const struct mmpdu_header *client_frame,
|
||||
size_t client_frame_len)
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
|
||||
static size_t ap_write_wsc_ie(struct ap_state *ap,
|
||||
enum mpdu_management_subtype type,
|
||||
const struct mmpdu_header *client_frame,
|
||||
size_t client_frame_len,
|
||||
uint8_t *out_buf)
|
||||
{
|
||||
const uint8_t *from = client_frame->address_2;
|
||||
uint8_t *wsc_data;
|
||||
size_t wsc_data_size;
|
||||
uint8_t *wsc_ie;
|
||||
size_t wsc_ie_size;
|
||||
size_t len = 0;
|
||||
|
||||
/* WSC IE */
|
||||
if (type == MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE) {
|
||||
struct wsc_probe_response wsc_pr = {};
|
||||
const struct mmpdu_probe_request *req =
|
||||
mmpdu_body(client_frame);
|
||||
size_t req_ies_len = (void *) client_frame + client_frame_len -
|
||||
(void *) req->ies;
|
||||
ssize_t req_wsc_data_size;
|
||||
|
||||
/*
|
||||
* Process the client Probe Request WSC IE first as it may
|
||||
* cause us to exit "active PBC mode" and that will be
|
||||
* immediately reflected in our Probe Response WSC IE.
|
||||
*/
|
||||
wsc_data = ie_tlv_extract_wsc_payload(req->ies, req_ies_len,
|
||||
&req_wsc_data_size);
|
||||
if (wsc_data) {
|
||||
ap_process_wsc_probe_req(ap, from, wsc_data,
|
||||
req_wsc_data_size);
|
||||
l_free(wsc_data);
|
||||
}
|
||||
|
||||
wsc_pr.version2 = true;
|
||||
wsc_pr.state = WSC_STATE_CONFIGURED;
|
||||
|
||||
if (ap->wsc_pbc_timeout) {
|
||||
wsc_pr.selected_registrar = true;
|
||||
wsc_pr.device_password_id = ap->wsc_dpid;
|
||||
wsc_pr.selected_reg_config_methods =
|
||||
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||
}
|
||||
|
||||
wsc_pr.response_type = WSC_RESPONSE_TYPE_AP;
|
||||
memcpy(wsc_pr.uuid_e, ap->wsc_uuid_r, sizeof(wsc_pr.uuid_e));
|
||||
wsc_pr.primary_device_type =
|
||||
ap->config->wsc_primary_device_type;
|
||||
|
||||
if (ap->config->wsc_name)
|
||||
l_strlcpy(wsc_pr.device_name, ap->config->wsc_name,
|
||||
sizeof(wsc_pr.device_name));
|
||||
|
||||
wsc_pr.config_methods =
|
||||
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||
|
||||
if (ap->config->authorized_macs_num) {
|
||||
size_t len;
|
||||
|
||||
len = ap->config->authorized_macs_num * 6;
|
||||
if (len > sizeof(wsc_pr.authorized_macs))
|
||||
len = sizeof(wsc_pr.authorized_macs);
|
||||
|
||||
memcpy(wsc_pr.authorized_macs,
|
||||
ap->config->authorized_macs, len);
|
||||
}
|
||||
|
||||
wsc_data = wsc_build_probe_response(&wsc_pr, &wsc_data_size);
|
||||
} else if (type == MPDU_MANAGEMENT_SUBTYPE_BEACON) {
|
||||
struct wsc_beacon wsc_beacon = {};
|
||||
|
||||
wsc_beacon.version2 = true;
|
||||
wsc_beacon.state = WSC_STATE_CONFIGURED;
|
||||
|
||||
if (ap->wsc_pbc_timeout) {
|
||||
wsc_beacon.selected_registrar = true;
|
||||
wsc_beacon.device_password_id = ap->wsc_dpid;
|
||||
wsc_beacon.selected_reg_config_methods =
|
||||
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||
}
|
||||
|
||||
if (ap->config->authorized_macs_num) {
|
||||
size_t len;
|
||||
|
||||
len = ap->config->authorized_macs_num * 6;
|
||||
if (len > sizeof(wsc_beacon.authorized_macs))
|
||||
len = sizeof(wsc_beacon.authorized_macs);
|
||||
|
||||
memcpy(wsc_beacon.authorized_macs,
|
||||
ap->config->authorized_macs, len);
|
||||
}
|
||||
|
||||
wsc_data = wsc_build_beacon(&wsc_beacon, &wsc_data_size);
|
||||
} else if (L_IN_SET(type, MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE,
|
||||
MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE)) {
|
||||
struct wsc_association_response wsc_resp = {};
|
||||
struct sta_state *sta =
|
||||
l_queue_find(ap->sta_states, ap_sta_match_addr, from);
|
||||
|
||||
if (!sta || sta->assoc_rsne)
|
||||
return 0;
|
||||
|
||||
wsc_resp.response_type = WSC_RESPONSE_TYPE_AP;
|
||||
wsc_resp.version2 = sta->wsc_v2;
|
||||
|
||||
wsc_data = wsc_build_association_response(&wsc_resp,
|
||||
&wsc_data_size);
|
||||
} else
|
||||
return 0;
|
||||
|
||||
if (!wsc_data) {
|
||||
l_error("wsc_build_<mgmt-subtype> error (stype 0x%x)", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_size,
|
||||
&wsc_ie_size);
|
||||
l_free(wsc_data);
|
||||
|
||||
if (!wsc_ie) {
|
||||
l_error("ie_tlv_encapsulate_wsc_payload error (stype 0x%x)",
|
||||
type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(out_buf + len, wsc_ie, wsc_ie_size);
|
||||
len += wsc_ie_size;
|
||||
l_free(wsc_ie);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t ap_get_extra_ies_len(struct ap_state *ap,
|
||||
enum mpdu_management_subtype type,
|
||||
const struct mmpdu_header *client_frame,
|
||||
@ -474,6 +757,8 @@ static size_t ap_get_extra_ies_len(struct ap_state *ap,
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
len += ap_get_wsc_ie_len(ap, type, client_frame, client_frame_len);
|
||||
|
||||
if (ap->ops->get_extra_ies_len)
|
||||
len += ap->ops->get_extra_ies_len(type, client_frame,
|
||||
client_frame_len,
|
||||
@ -490,6 +775,9 @@ static size_t ap_write_extra_ies(struct ap_state *ap,
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
len += ap_write_wsc_ie(ap, type, client_frame, client_frame_len,
|
||||
out_buf + len);
|
||||
|
||||
if (ap->ops->write_extra_ies)
|
||||
len += ap->ops->write_extra_ies(type,
|
||||
client_frame, client_frame_len,
|
||||
@ -581,10 +869,6 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap,
|
||||
{
|
||||
size_t len;
|
||||
struct ie_rsn_info rsn;
|
||||
uint8_t *wsc_data;
|
||||
size_t wsc_data_size;
|
||||
uint8_t *wsc_ie;
|
||||
size_t wsc_ie_size;
|
||||
|
||||
/* TODO: Country IE between TIM IE and RSNE */
|
||||
|
||||
@ -594,85 +878,6 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap,
|
||||
return 0;
|
||||
len = 2 + out_buf[1];
|
||||
|
||||
/* WSC IE */
|
||||
if (stype == MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE) {
|
||||
struct wsc_probe_response wsc_pr = {};
|
||||
|
||||
wsc_pr.version2 = true;
|
||||
wsc_pr.state = WSC_STATE_CONFIGURED;
|
||||
|
||||
if (ap->wsc_pbc_timeout) {
|
||||
wsc_pr.selected_registrar = true;
|
||||
wsc_pr.device_password_id = ap->wsc_dpid;
|
||||
wsc_pr.selected_reg_config_methods =
|
||||
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||
}
|
||||
|
||||
wsc_pr.response_type = WSC_RESPONSE_TYPE_AP;
|
||||
memcpy(wsc_pr.uuid_e, ap->wsc_uuid_r, sizeof(wsc_pr.uuid_e));
|
||||
wsc_pr.primary_device_type =
|
||||
ap->config->wsc_primary_device_type;
|
||||
|
||||
if (ap->config->wsc_name)
|
||||
l_strlcpy(wsc_pr.device_name, ap->config->wsc_name,
|
||||
sizeof(wsc_pr.device_name));
|
||||
|
||||
wsc_pr.config_methods =
|
||||
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||
|
||||
if (ap->config->authorized_macs_num) {
|
||||
size_t len;
|
||||
|
||||
len = ap->config->authorized_macs_num * 6;
|
||||
if (len > sizeof(wsc_pr.authorized_macs))
|
||||
len = sizeof(wsc_pr.authorized_macs);
|
||||
|
||||
memcpy(wsc_pr.authorized_macs,
|
||||
ap->config->authorized_macs, len);
|
||||
}
|
||||
|
||||
wsc_data = wsc_build_probe_response(&wsc_pr, &wsc_data_size);
|
||||
} else {
|
||||
struct wsc_beacon wsc_beacon = {};
|
||||
|
||||
wsc_beacon.version2 = true;
|
||||
wsc_beacon.state = WSC_STATE_CONFIGURED;
|
||||
|
||||
if (ap->wsc_pbc_timeout) {
|
||||
wsc_beacon.selected_registrar = true;
|
||||
wsc_beacon.device_password_id = ap->wsc_dpid;
|
||||
wsc_beacon.selected_reg_config_methods =
|
||||
WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
|
||||
}
|
||||
|
||||
if (ap->config->authorized_macs_num) {
|
||||
size_t len;
|
||||
|
||||
len = ap->config->authorized_macs_num * 6;
|
||||
if (len > sizeof(wsc_beacon.authorized_macs))
|
||||
len = sizeof(wsc_beacon.authorized_macs);
|
||||
|
||||
memcpy(wsc_beacon.authorized_macs,
|
||||
ap->config->authorized_macs, len);
|
||||
}
|
||||
|
||||
wsc_data = wsc_build_beacon(&wsc_beacon, &wsc_data_size);
|
||||
}
|
||||
|
||||
if (!wsc_data)
|
||||
return 0;
|
||||
|
||||
wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_size,
|
||||
&wsc_ie_size);
|
||||
l_free(wsc_data);
|
||||
|
||||
if (!wsc_ie)
|
||||
return 0;
|
||||
|
||||
memcpy(out_buf + len, wsc_ie, wsc_ie_size);
|
||||
len += wsc_ie_size;
|
||||
l_free(wsc_ie);
|
||||
|
||||
len += ap_write_extra_ies(ap, stype, req, req_len, out_buf + len);
|
||||
return len;
|
||||
}
|
||||
@ -725,18 +930,6 @@ void ap_update_beacon(struct ap_state *ap)
|
||||
l_error("Issuing SET_BEACON failed");
|
||||
}
|
||||
|
||||
static void ap_wsc_exit_pbc(struct ap_state *ap)
|
||||
{
|
||||
if (!ap->wsc_pbc_timeout)
|
||||
return;
|
||||
|
||||
l_timeout_remove(ap->wsc_pbc_timeout);
|
||||
ap->wsc_dpid = 0;
|
||||
ap_update_beacon(ap);
|
||||
|
||||
ap->ops->handle_event(AP_EVENT_PBC_MODE_EXIT, NULL, ap->user_data);
|
||||
}
|
||||
|
||||
static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
|
||||
const struct mmpdu_header *frame,
|
||||
size_t frame_len,
|
||||
@ -859,24 +1052,6 @@ error:
|
||||
ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
|
||||
}
|
||||
|
||||
struct ap_pbc_record_expiry_data {
|
||||
uint64_t min_time;
|
||||
const uint8_t *mac;
|
||||
};
|
||||
|
||||
static bool ap_wsc_pbc_record_expire(void *data, void *user_data)
|
||||
{
|
||||
struct ap_wsc_pbc_probe_record *record = data;
|
||||
const struct ap_pbc_record_expiry_data *expiry_data = user_data;
|
||||
|
||||
if (record->timestamp > expiry_data->min_time &&
|
||||
memcmp(record->mac, expiry_data->mac, 6))
|
||||
return false;
|
||||
|
||||
l_free(record);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ap_stop_handshake_schedule(struct sta_state *sta)
|
||||
{
|
||||
if (sta->stop_handshake_work)
|
||||
@ -1294,41 +1469,9 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
|
||||
resp->ies[ies_len++] = count;
|
||||
ies_len += count;
|
||||
|
||||
if (sta && !sta->assoc_rsne) {
|
||||
struct wsc_association_response wsc_resp = {};
|
||||
uint8_t *wsc_data;
|
||||
size_t wsc_data_len;
|
||||
uint8_t *wsc_ie;
|
||||
size_t wsc_ie_len;
|
||||
|
||||
wsc_resp.response_type = WSC_RESPONSE_TYPE_AP;
|
||||
wsc_resp.version2 = sta->wsc_v2;
|
||||
|
||||
wsc_data = wsc_build_association_response(&wsc_resp,
|
||||
&wsc_data_len);
|
||||
if (!wsc_data) {
|
||||
l_error("wsc_build_beacon error");
|
||||
goto send_frame;
|
||||
}
|
||||
|
||||
wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_len,
|
||||
&wsc_ie_len);
|
||||
l_free(wsc_data);
|
||||
|
||||
if (!wsc_ie) {
|
||||
l_error("ie_tlv_encapsulate_wsc_payload error");
|
||||
goto send_frame;
|
||||
}
|
||||
|
||||
memcpy(resp->ies + ies_len, wsc_ie, wsc_ie_len);
|
||||
ies_len += wsc_ie_len;
|
||||
l_free(wsc_ie);
|
||||
}
|
||||
|
||||
ies_len += ap_write_extra_ies(ap, stype, req, req_len,
|
||||
resp->ies + ies_len);
|
||||
|
||||
send_frame:
|
||||
return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
|
||||
callback, sta);
|
||||
}
|
||||
@ -1705,118 +1848,6 @@ bad_frame:
|
||||
l_error("Sending error Reassociation Response failed");
|
||||
}
|
||||
|
||||
#define AP_WSC_PBC_MONITOR_TIME 120
|
||||
#define AP_WSC_PBC_WALK_TIME 120
|
||||
|
||||
static void ap_process_wsc_probe_req(struct ap_state *ap, const uint8_t *from,
|
||||
const uint8_t *wsc_data,
|
||||
size_t wsc_data_len)
|
||||
{
|
||||
struct wsc_probe_request req;
|
||||
struct ap_pbc_record_expiry_data expiry_data;
|
||||
struct ap_wsc_pbc_probe_record *record;
|
||||
uint64_t now;
|
||||
bool empty;
|
||||
uint8_t first_sta_addr[6] = {};
|
||||
const struct l_queue_entry *entry;
|
||||
|
||||
if (wsc_parse_probe_request(wsc_data, wsc_data_len, &req) < 0)
|
||||
return;
|
||||
|
||||
if (!(req.config_methods & WSC_CONFIGURATION_METHOD_PUSH_BUTTON))
|
||||
return;
|
||||
|
||||
if (req.device_password_id != WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON)
|
||||
return;
|
||||
|
||||
/* Save the address of the first enrollee record */
|
||||
record = l_queue_peek_head(ap->wsc_pbc_probes);
|
||||
if (record)
|
||||
memcpy(first_sta_addr, record->mac, 6);
|
||||
|
||||
now = l_time_now();
|
||||
|
||||
/*
|
||||
* Expire entries older than PBC Monitor Time. While there also drop
|
||||
* older entries from the same Enrollee that sent us this new Probe
|
||||
* Request. It's unclear whether we should also match by the UUID-E.
|
||||
*/
|
||||
expiry_data.min_time = now - AP_WSC_PBC_MONITOR_TIME * 1000000;
|
||||
expiry_data.mac = from;
|
||||
l_queue_foreach_remove(ap->wsc_pbc_probes, ap_wsc_pbc_record_expire,
|
||||
&expiry_data);
|
||||
|
||||
empty = l_queue_isempty(ap->wsc_pbc_probes);
|
||||
|
||||
if (!ap->wsc_pbc_probes)
|
||||
ap->wsc_pbc_probes = l_queue_new();
|
||||
|
||||
/* Add new record */
|
||||
record = l_new(struct ap_wsc_pbc_probe_record, 1);
|
||||
memcpy(record->mac, from, 6);
|
||||
memcpy(record->uuid_e, req.uuid_e, sizeof(record->uuid_e));
|
||||
record->timestamp = now;
|
||||
l_queue_push_tail(ap->wsc_pbc_probes, record);
|
||||
|
||||
/*
|
||||
* If queue was non-empty and we've added one more record then we
|
||||
* now have seen more than one PBC enrollee during the PBC Monitor
|
||||
* Time and must exit "active PBC mode" due to "session overlap".
|
||||
* WSC v2.0.5 Section 11.3:
|
||||
* "Within the PBC Monitor Time, if the Registrar receives PBC
|
||||
* probe requests from more than one Enrollee [...] then the
|
||||
* Registrar SHALL signal a "session overlap" error. As a result,
|
||||
* the Registrar shall refuse to enter active PBC mode and shall
|
||||
* also refuse to perform a PBC-based Registration Protocol
|
||||
* exchange [...]"
|
||||
*/
|
||||
if (empty)
|
||||
return;
|
||||
|
||||
if (ap->wsc_pbc_timeout) {
|
||||
l_debug("Exiting PBC mode due to Session Overlap");
|
||||
ap_wsc_exit_pbc(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* "If the Registrar is engaged in PBC Registration Protocol
|
||||
* exchange with an Enrollee and receives a Probe Request or M1
|
||||
* Message from another Enrollee, then the Registrar should
|
||||
* signal a "session overlap" error".
|
||||
*
|
||||
* For simplicity just interrupt the handshake with that enrollee.
|
||||
*/
|
||||
for (entry = l_queue_get_entries(ap->sta_states); entry;
|
||||
entry = entry->next) {
|
||||
struct sta_state *sta = entry->data;
|
||||
|
||||
if (!sta->associated || sta->assoc_rsne)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check whether this enrollee is in PBC Registration
|
||||
* Protocol by comparing its mac with the first (and only)
|
||||
* record we had in ap->wsc_pbc_probes. If we had more
|
||||
* than one record we wouldn't have been in
|
||||
* "active PBC mode".
|
||||
*/
|
||||
if (memcmp(sta->addr, first_sta_addr, 6) ||
|
||||
!memcmp(sta->addr, from, 6))
|
||||
continue;
|
||||
|
||||
l_debug("Interrupting handshake with %s due to Session Overlap",
|
||||
util_address_to_string(sta->addr));
|
||||
|
||||
if (sta->hs) {
|
||||
netdev_handshake_failed(sta->hs,
|
||||
MMPDU_REASON_CODE_DISASSOC_AP_BUSY);
|
||||
sta->sm = NULL;
|
||||
}
|
||||
|
||||
ap_remove_sta(sta);
|
||||
}
|
||||
}
|
||||
|
||||
static void ap_probe_resp_cb(int err, void *user_data)
|
||||
{
|
||||
if (err == -ECOMM)
|
||||
@ -1848,8 +1879,6 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
|
||||
l_malloc(512 + ap_get_extra_ies_len(ap,
|
||||
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE, hdr,
|
||||
body + body_len - (void *) hdr));
|
||||
uint8_t *wsc_data;
|
||||
ssize_t wsc_data_len;
|
||||
|
||||
l_info("AP Probe Request from %s",
|
||||
util_address_to_string(hdr->address_2));
|
||||
@ -1922,18 +1951,6 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Process the WSC IE first as it may cause us to exit "active PBC
|
||||
* mode" and that can be immediately reflected in our Probe Response.
|
||||
*/
|
||||
wsc_data = ie_tlv_extract_wsc_payload(req->ies, body_len - sizeof(*req),
|
||||
&wsc_data_len);
|
||||
if (wsc_data) {
|
||||
ap_process_wsc_probe_req(ap, hdr->address_2,
|
||||
wsc_data, wsc_data_len);
|
||||
l_free(wsc_data);
|
||||
}
|
||||
|
||||
len = ap_build_beacon_pr_head(ap,
|
||||
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
|
||||
hdr->address_2, resp, sizeof(resp));
|
||||
|
Loading…
Reference in New Issue
Block a user