mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 18:59:22 +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;
|
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,
|
static size_t ap_get_extra_ies_len(struct ap_state *ap,
|
||||||
enum mpdu_management_subtype type,
|
enum mpdu_management_subtype type,
|
||||||
const struct mmpdu_header *client_frame,
|
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;
|
size_t len = 0;
|
||||||
|
|
||||||
|
len += ap_get_wsc_ie_len(ap, type, client_frame, client_frame_len);
|
||||||
|
|
||||||
if (ap->ops->get_extra_ies_len)
|
if (ap->ops->get_extra_ies_len)
|
||||||
len += ap->ops->get_extra_ies_len(type, client_frame,
|
len += ap->ops->get_extra_ies_len(type, client_frame,
|
||||||
client_frame_len,
|
client_frame_len,
|
||||||
@ -490,6 +775,9 @@ static size_t ap_write_extra_ies(struct ap_state *ap,
|
|||||||
{
|
{
|
||||||
size_t len = 0;
|
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)
|
if (ap->ops->write_extra_ies)
|
||||||
len += ap->ops->write_extra_ies(type,
|
len += ap->ops->write_extra_ies(type,
|
||||||
client_frame, client_frame_len,
|
client_frame, client_frame_len,
|
||||||
@ -581,10 +869,6 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap,
|
|||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
struct ie_rsn_info rsn;
|
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 */
|
/* 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;
|
return 0;
|
||||||
len = 2 + out_buf[1];
|
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);
|
len += ap_write_extra_ies(ap, stype, req, req_len, out_buf + len);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@ -725,18 +930,6 @@ void ap_update_beacon(struct ap_state *ap)
|
|||||||
l_error("Issuing SET_BEACON failed");
|
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,
|
static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
|
||||||
const struct mmpdu_header *frame,
|
const struct mmpdu_header *frame,
|
||||||
size_t frame_len,
|
size_t frame_len,
|
||||||
@ -859,24 +1052,6 @@ error:
|
|||||||
ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
|
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)
|
static void ap_stop_handshake_schedule(struct sta_state *sta)
|
||||||
{
|
{
|
||||||
if (sta->stop_handshake_work)
|
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;
|
resp->ies[ies_len++] = count;
|
||||||
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,
|
ies_len += ap_write_extra_ies(ap, stype, req, req_len,
|
||||||
resp->ies + ies_len);
|
resp->ies + ies_len);
|
||||||
|
|
||||||
send_frame:
|
|
||||||
return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
|
return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
|
||||||
callback, sta);
|
callback, sta);
|
||||||
}
|
}
|
||||||
@ -1705,118 +1848,6 @@ bad_frame:
|
|||||||
l_error("Sending error Reassociation Response failed");
|
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)
|
static void ap_probe_resp_cb(int err, void *user_data)
|
||||||
{
|
{
|
||||||
if (err == -ECOMM)
|
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,
|
l_malloc(512 + ap_get_extra_ies_len(ap,
|
||||||
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE, hdr,
|
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE, hdr,
|
||||||
body + body_len - (void *) hdr));
|
body + body_len - (void *) hdr));
|
||||||
uint8_t *wsc_data;
|
|
||||||
ssize_t wsc_data_len;
|
|
||||||
|
|
||||||
l_info("AP Probe Request from %s",
|
l_info("AP Probe Request from %s",
|
||||||
util_address_to_string(hdr->address_2));
|
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)
|
if (!match)
|
||||||
return;
|
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,
|
len = ap_build_beacon_pr_head(ap,
|
||||||
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
|
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
|
||||||
hdr->address_2, resp, sizeof(resp));
|
hdr->address_2, resp, sizeof(resp));
|
||||||
|
Loading…
Reference in New Issue
Block a user